diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ValidationError.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ValidationError.java
--- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ValidationError.java
+++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ValidationError.java
@@ -79,7 +79,6 @@ public enum ErrorType {
     VALUE_OUT_OF_BOUNDS,
     INVALID_URL,
     INVALID_PARTITIONING,
-    TOO_MANY_DECIMAL_PLACES,
-    MISSING_ENTRY
+    TOO_MANY_DECIMAL_PLACES
   }
 }

diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ExposureConfigurationValidatorTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ExposureConfigurationValidatorTest.java
--- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ExposureConfigurationValidatorTest.java
+++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ExposureConfigurationValidatorTest.java
@@ -22,7 +22,6 @@

 import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ExposureConfigurationValidator.CONFIG_PREFIX;
 import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.RiskScoreClassificationValidatorTest.buildError;
-import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ValidationError.ErrorType.MISSING_ENTRY;
 import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ValidationError.ErrorType.TOO_MANY_DECIMAL_PLACES;
 import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ValidationError.ErrorType.VALUE_OUT_OF_BOUNDS;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -69,8 +68,6 @@ private static Stream<Arguments> createOkTests() {
   private static Stream<Arguments> createFailedTests() {
     return Stream.of(
         ScoreTooHigh(),
-        // TODO cwa-server/#320 Validate that no attributes are missing in .yaml
-        // ScoreNegative(),
         WeightNegative(),
         WeightTooHigh()
     ).map(Arguments::of);
@@ -103,12 +100,6 @@ public static TestWithExpectedResult WeightTooHigh() {
         .with(buildError(CONFIG_PREFIX + "transmission", 101d, VALUE_OUT_OF_BOUNDS));
   }

-  public static TestWithExpectedResult ScoreNegative() {
-    return new TestWithExpectedResult("score_negative.yaml")
-        .with(buildError(CONFIG_PREFIX + "transmission.appDefined1", RiskLevel.UNRECOGNIZED, VALUE_OUT_OF_BOUNDS))
-        .with(buildError(CONFIG_PREFIX + "transmission.appDefined3", null, MISSING_ENTRY));
-  }
-
   public static TestWithExpectedResult ScoreTooHigh() {
     return new TestWithExpectedResult("score_too_high.yaml")
         .with(buildError(CONFIG_PREFIX + "transmission.appDefined1", RiskLevel.UNRECOGNIZED, VALUE_OUT_OF_BOUNDS))
diff --git a/services/distribution/src/test/resources/parameters/score_negative.yaml b/services/distribution/src/test/resources/parameters/score_negative.yaml
deleted file mode 100644
--- a/services/distribution/src/test/resources/parameters/score_negative.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-transmission_weight: 100
-duration_weight: 50
-attenuation_weight: 80
-
-
-transmission:
-  app_defined_1: -2 # not ok, negative
-  app_defined_2: 0
-  #app_defined_3: 3 # not ok (missing -> unspecified)
-  app_defined_4: 5
-  app_defined_5: 5
-  app_defined_6: 6
-  app_defined_7: 7
-  app_defined_8: 8
-
-duration:
-  eq_0_min: 1
-  gt_0_le_5_min: 2
-  gt_5_le_10_min: 3
-  gt_10_le_15_min: 4
-  gt_15_le_20_min: 5
-  gt_20_le_25_min: 6
-  gt_25_le_30_min: 7
-  gt_30_min: 8
-
-days_since_last_exposure:
-  ge_14_days: 1
-  ge_12_lt_14_days: 2
-  ge_10_lt_12_days: 3
-  ge_8_lt_10_days: 4
-  ge_6_lt_8_days: 5
-  ge_4_lt_6_days: 6
-  ge_2_lt_4_days: 7
-  ge_0_lt_2_days: 8
-
-attenuation:
-  gt_73_dbm: 1
-  gt_63_le_73_dbm: 2
-  gt_51_le_63_dbm: 3
-  gt_33_le_51_dbm: 4
-  gt_27_le_33_dbm: 5
-  gt_15_le_27_dbm: 6
-  gt_10_le_15_dbm: 7
-  lt_10_dbm: 8

Validate that no attributes are missing in .yaml
validate that no attributes are missing in .yaml when accordingly re-enable the ``ScoreNegative`` test case in:
https://github.com/corona-warn-app/cwa-server/blob/master/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ExposureConfigurationValidatorTest.java

Note: the validation probably should be done on the loading the values from yaml into a proto object, since validating directly on the resulting proto object is too late as the default value for an unloaded attribute in proto is 0 which we consider as a valid value (or risk score in the allowed range of 0-8)

See PR which disabled the test: https://github.com/corona-warn-app/cwa-server/pull/299


diff --git a/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/submission_payload.proto b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/submission_payload.proto
--- a/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/submission_payload.proto
+++ b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/submission_payload.proto
@@ -6,4 +6,5 @@ import "app/coronawarn/server/common/protocols/external/exposurenotification/tem

 message SubmissionPayload {
   repeated app.coronawarn.server.common.protocols.external.exposurenotification.TemporaryExposureKey keys = 1;
-}
\ No newline at end of file
+  bytes padding = 2;
+}
diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java
--- a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java
+++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java
@@ -20,6 +20,7 @@

 package app.coronawarn.server.services.submission;

+import app.coronawarn.server.services.submission.config.SubmissionServiceConfigValidator;
 import io.micrometer.core.aop.TimedAspect;
 import io.micrometer.core.instrument.MeterRegistry;
 import java.util.Arrays;
@@ -40,6 +41,7 @@
 import org.springframework.core.env.Environment;
 import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
 import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
+import org.springframework.validation.Validator;

 @SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
 @EnableJdbcRepositories(basePackages = "app.coronawarn.server.common.persistence")
@@ -74,6 +76,11 @@ ProtobufHttpMessageConverter protobufHttpMessageConverter() {
     return new ProtobufHttpMessageConverter();
   }

+  @Bean
+  public static Validator configurationPropertiesValidator() {
+    return new SubmissionServiceConfigValidator();
+  }
+
   @Override
   public void setEnvironment(Environment environment) {
     List<String> profiles = Arrays.asList(environment.getActiveProfiles());
diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionPayloadSizeFilter.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionPayloadSizeFilter.java
new file mode 100644
--- /dev/null
+++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionPayloadSizeFilter.java
@@ -0,0 +1,58 @@
+/*-
+ * ---license-start
+ * Corona-Warn-App
+ * ---
+ * Copyright (C) 2020 SAP SE and all other contributors
+ * ---
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ---license-end
+ */
+
+package app.coronawarn.server.services.submission.config;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+/**
+ * {@link SubmissionPayloadSizeFilter} instances filter requests exceeding a certain size limit.
+ */
+@Component
+public class SubmissionPayloadSizeFilter extends OncePerRequestFilter {
+
+  private final long maximumRequestSize;
+
+  public SubmissionPayloadSizeFilter(SubmissionServiceConfig config) {
+    this.maximumRequestSize = config.getMaximumRequestSize().toBytes();
+  }
+
+  /**
+   * Filters each request that exceeds the maximum size.
+   */
+  @Override
+  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+      throws ServletException, IOException {
+    if (request.getContentLengthLong() > maximumRequestSize) {
+      response.setStatus(HttpStatus.BAD_REQUEST.value());
+      response.getWriter().write("Request size exceeded limit of " + maximumRequestSize + " bytes");
+    } else {
+      filterChain.doFilter(request, response);
+    }
+  }
+
+}
diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java
--- a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java
+++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfig.java
@@ -26,6 +26,7 @@
 import javax.validation.constraints.Pattern;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
+import org.springframework.util.unit.DataSize;
 import org.springframework.validation.annotation.Validated;

 @Component
@@ -53,6 +54,7 @@ public class SubmissionServiceConfig {
   @Min(1)
   @Max(10000)
   private Integer connectionPoolSize;
+  private DataSize maximumRequestSize;
   private Payload payload;
   private Verification verification;
   private Monitoring monitoring;
@@ -98,6 +100,14 @@ public void setConnectionPoolSize(Integer connectionPoolSize) {
     this.connectionPoolSize = connectionPoolSize;
   }

+  public DataSize getMaximumRequestSize() {
+    return maximumRequestSize;
+  }
+
+  public void setMaximumRequestSize(DataSize maximumRequestSize) {
+    this.maximumRequestSize = maximumRequestSize;
+  }
+
   public Integer getMaxNumberOfKeys() {
     return payload.getMaxNumberOfKeys();
   }
diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidator.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidator.java
new file mode 100644
--- /dev/null
+++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidator.java
@@ -0,0 +1,54 @@
+/*-
+ * ---license-start
+ * Corona-Warn-App
+ * ---
+ * Copyright (C) 2020 SAP SE and all other contributors
+ * ---
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ---license-end
+ */
+
+package app.coronawarn.server.services.submission.config;
+
+import org.springframework.util.unit.DataSize;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
+
+/**
+ * {@link SubmissionPayloadSizeFilter} instances validate the values of the SubmissionServiceConfig.
+ */
+public class SubmissionServiceConfigValidator implements Validator {
+
+  public static final DataSize MIN_MAXIMUM_REQUEST_SIZE = DataSize.ofBytes(280);
+  public static final DataSize MAX_MAXIMUM_REQUEST_SIZE = DataSize.ofKilobytes(200);
+
+  @Override
+  public boolean supports(Class<?> type) {
+    return type == SubmissionServiceConfig.class;
+  }
+
+  /**
+   * Validate if the MaximumRequestSize of the {@link SubmissionServiceConfig} is in the defined range.
+   */
+  @Override
+  public void validate(Object o, Errors errors) {
+    SubmissionServiceConfig properties = (SubmissionServiceConfig) o;
+
+    if (properties.getMaximumRequestSize().compareTo(MIN_MAXIMUM_REQUEST_SIZE) < 0
+        || properties.getMaximumRequestSize().compareTo(MAX_MAXIMUM_REQUEST_SIZE) > 0) {
+      errors.rejectValue("maximumRequestSize",
+          "Must be at least " + MIN_MAXIMUM_REQUEST_SIZE + " and not more than " + MAX_MAXIMUM_REQUEST_SIZE + ".");
+    }
+  }
+
+}
diff --git a/services/submission/src/main/resources/application.yaml b/services/submission/src/main/resources/application.yaml
--- a/services/submission/src/main/resources/application.yaml
+++ b/services/submission/src/main/resources/application.yaml
@@ -23,6 +23,8 @@ services:
     random-key-padding-multiplier: ${RANDOM_KEY_PADDING_MULTIPLIER:1}
     # The ApacheHttpClient's connection pool size.
     connection-pool-size: 200
+    # The maximum request size accepted by the SubmissionController (e.g. 200B or 100KB).
+    maximum-request-size: ${MAXIMUM_REQUEST_SIZE:100KB}
     payload:
       # The maximum number of keys accepted for any submission.
       max-number-of-keys: 14

diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidatorTest.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidatorTest.java
new file mode 100644
--- /dev/null
+++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/config/SubmissionServiceConfigValidatorTest.java
@@ -0,0 +1,86 @@
+/*-
+ * ---license-start
+ * Corona-Warn-App
+ * ---
+ * Copyright (C) 2020 SAP SE and all other contributors
+ * ---
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ---license-end
+ */
+
+package app.coronawarn.server.services.submission.config;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.util.unit.DataSize;
+import org.springframework.validation.BeanPropertyBindingResult;
+import org.springframework.validation.Errors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles({"disable-ssl-client-verification", "disable-ssl-client-verification-verify-hostname"})
+class SubmissionServiceConfigValidatorTest {
+
+  @Autowired
+  private SubmissionServiceConfigValidator submissionServiceConfigValidator;
+
+  private SubmissionServiceConfig submissionServiceConfig;
+
+  @BeforeEach
+  void setup() {
+    submissionServiceConfig = new SubmissionServiceConfig();
+  }
+
+  @ParameterizedTest
+  @MethodSource("validRequestDataSizes")
+  void ok(DataSize dataSize) {
+    Errors errors = validateConfig(dataSize);
+    assertThat(errors.hasErrors()).isFalse();
+  }
+
+  @ParameterizedTest
+  @MethodSource("invalidRequestDataSizes")
+  void fail(DataSize dataSize) {
+    Errors errors = validateConfig(dataSize);
+    assertThat(errors.hasErrors()).isTrue();
+  }
+
+  private Errors validateConfig(DataSize dataSize) {
+    Errors errors = new BeanPropertyBindingResult(submissionServiceConfig, "submissionServiceConfig");
+    submissionServiceConfig.setMaximumRequestSize(dataSize);
+    submissionServiceConfigValidator.validate(submissionServiceConfig, errors);
+    return errors;
+  }
+
+  private static Stream<Arguments> validRequestDataSizes() {
+    return Stream.of(
+        SubmissionServiceConfigValidator.MAX_MAXIMUM_REQUEST_SIZE,
+        SubmissionServiceConfigValidator.MIN_MAXIMUM_REQUEST_SIZE
+    ).map(Arguments::of);
+  }
+
+  private static Stream<Arguments> invalidRequestDataSizes() {
+    return Stream.of(
+        DataSize.ofBytes(SubmissionServiceConfigValidator.MAX_MAXIMUM_REQUEST_SIZE.toBytes() + 1),
+        DataSize.ofBytes(SubmissionServiceConfigValidator.MIN_MAXIMUM_REQUEST_SIZE.toBytes() - 1)
+    ).map(Arguments::of);
+  }
+}
diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java
--- a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java
+++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/RequestExecutor.java
@@ -62,6 +62,10 @@ public ResponseEntity<Void> execute(HttpMethod method, RequestEntity<SubmissionP

   public ResponseEntity<Void> executePost(Collection<TemporaryExposureKey> keys, HttpHeaders headers) {
     SubmissionPayload body = SubmissionPayload.newBuilder().addAllKeys(keys).build();
+    return executePost(body, headers);
+  }
+
+  public ResponseEntity<Void> executePost(SubmissionPayload body, HttpHeaders headers) {
     return execute(HttpMethod.POST, new RequestEntity<>(body, headers, HttpMethod.POST, SUBMISSION_URL));
   }

diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java
--- a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java
+++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java
@@ -45,9 +45,12 @@
 import static org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED;
 import static org.springframework.http.HttpStatus.OK;

+
 import app.coronawarn.server.common.persistence.domain.DiagnosisKey;
 import app.coronawarn.server.common.persistence.service.DiagnosisKeyService;
 import app.coronawarn.server.common.protocols.external.exposurenotification.TemporaryExposureKey;
+import app.coronawarn.server.common.protocols.internal.SubmissionPayload;
+import app.coronawarn.server.services.submission.config.SubmissionPayloadSizeFilter;
 import app.coronawarn.server.services.submission.config.SubmissionServiceConfig;
 import app.coronawarn.server.services.submission.monitoring.SubmissionMonitor;
 import app.coronawarn.server.services.submission.verification.TanVerifier;
@@ -104,7 +107,13 @@ public void setUpMocks() {

   @Test
   void checkResponseStatusForValidParameters() {
-    ResponseEntity<Void> actResponse = executor.executePost(buildPayloadWithMultipleKeys(), buildOkHeaders());
+    ResponseEntity<Void> actResponse = executor.executePost(buildPayload(buildMultipleKeys()), buildOkHeaders());
+    assertThat(actResponse.getStatusCode()).isEqualTo(OK);
+  }
+
+  @Test
+  void checkResponseStatusForValidParametersWithPadding() {
+    ResponseEntity<Void> actResponse = executor.executePost(buildPayloadWithPadding(), buildOkHeaders());
     assertThat(actResponse.getStatusCode()).isEqualTo(OK);
   }

@@ -116,10 +125,9 @@ void check400ResponseStatusForInvalidParameters() {

   @Test
   void singleKeyWithOutdatedRollingStartIntervalNumberDoesNotGetSaved() {
-    Collection<TemporaryExposureKey> keys = buildPayloadWithSingleOutdatedKey();
     ArgumentCaptor<Collection<DiagnosisKey>> argument = ArgumentCaptor.forClass(Collection.class);

-    executor.executePost(keys, buildOkHeaders());
+    executor.executePost(buildPayload(createOutdatedKey()), buildOkHeaders());

     verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture());
     assertThat(argument.getValue()).isEmpty();
@@ -127,12 +135,12 @@ void singleKeyWithOutdatedRollingStartIntervalNumberDoesNotGetSaved() {

   @Test
   void keysWithOutdatedRollingStartIntervalNumberDoNotGetSaved() {
-    Collection<TemporaryExposureKey> submittedKeys = buildPayloadWithMultipleKeys();
+    Collection<TemporaryExposureKey> submittedKeys = buildMultipleKeys();
     TemporaryExposureKey outdatedKey = createOutdatedKey();
     submittedKeys.add(outdatedKey);
     ArgumentCaptor<Collection<DiagnosisKey>> argument = ArgumentCaptor.forClass(Collection.class);

-    executor.executePost(submittedKeys, buildOkHeaders());
+    executor.executePost(buildPayload(submittedKeys), buildOkHeaders());

     verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture());
     submittedKeys.remove(outdatedKey);
@@ -141,10 +149,10 @@ void keysWithOutdatedRollingStartIntervalNumberDoNotGetSaved() {

   @Test
   void checkSaveOperationCallAndFakeDelayUpdateForValidParameters() {
-    Collection<TemporaryExposureKey> submittedKeys = buildPayloadWithMultipleKeys();
+    Collection<TemporaryExposureKey> submittedKeys = buildMultipleKeys();
     ArgumentCaptor<Collection<DiagnosisKey>> argument = ArgumentCaptor.forClass(Collection.class);

-    executor.executePost(submittedKeys, buildOkHeaders());
+    executor.executePost(buildPayload(submittedKeys), buildOkHeaders());

     verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture());
     verify(fakeDelayManager, times(1)).updateFakeRequestDelay(anyLong());
@@ -200,6 +208,12 @@ void invalidTanHandling() {
     assertThat(actResponse.getStatusCode()).isEqualTo(FORBIDDEN);
   }

+  @Test
+  void invalidSubmissionPayload() {
+    ResponseEntity<Void> actResponse = executor.executePost(buildPayloadWithTooLargePadding(), buildOkHeaders());
+    assertThat(actResponse.getStatusCode()).isEqualTo(BAD_REQUEST);
+  }
+
   @Test
   void checkRealRequestHandlingIsMonitored() {
     executor.executePost(buildPayloadWithOneKey(), buildOkHeaders());
@@ -222,7 +236,35 @@ void checkInvalidTanHandlingIsMonitored() {
     verify(submissionMonitor, times(1)).incrementInvalidTanRequestCounter();
   }

-  private Collection<TemporaryExposureKey> buildPayloadWithMultipleKeys() {
+  private SubmissionPayload buildPayload(TemporaryExposureKey key) {
+    Collection<TemporaryExposureKey> keys = Stream.of(key).collect(Collectors.toCollection(ArrayList::new));
+    return buildPayload(keys);
+  }
+
+  private SubmissionPayload buildPayload(Collection<TemporaryExposureKey> keys) {
+    return SubmissionPayload.newBuilder()
+        .addAllKeys(keys)
+        .build();
+  }
+
+  private SubmissionPayload buildPayloadWithPadding() {
+    return SubmissionPayload.newBuilder()
+        .addAllKeys(buildMultipleKeys())
+        .setPadding(ByteString.copyFrom("PaddingString".getBytes()))
+        .build();
+  }
+
+  private SubmissionPayload buildPayloadWithTooLargePadding() {
+    int exceedingSize = (int) (2 * config.getMaximumRequestSize().toBytes());
+    byte[] bytes = new byte[exceedingSize];
+
+    return SubmissionPayload.newBuilder()
+        .addAllKeys(buildMultipleKeys())
+        .setPadding(ByteString.copyFrom(bytes))
+        .build();
+  }
+
+  private Collection<TemporaryExposureKey> buildMultipleKeys() {
     int rollingStartIntervalNumber1 = createRollingStartIntervalNumber(config.getRetentionDays() - 1);
     int rollingStartIntervalNumber2 = rollingStartIntervalNumber1 + DiagnosisKey.EXPECTED_ROLLING_PERIOD;
     int rollingStartIntervalNumber3 = rollingStartIntervalNumber2 + DiagnosisKey.EXPECTED_ROLLING_PERIOD;
@@ -233,11 +275,6 @@ private Collection<TemporaryExposureKey> buildPayloadWithMultipleKeys() {
         .collect(Collectors.toCollection(ArrayList::new));
   }

-  private Collection<TemporaryExposureKey> buildPayloadWithSingleOutdatedKey() {
-    TemporaryExposureKey outdatedKey = createOutdatedKey();
-    return Stream.of(outdatedKey).collect(Collectors.toCollection(ArrayList::new));
-  }
-
   private TemporaryExposureKey createOutdatedKey() {
     return TemporaryExposureKey.newBuilder()
         .setKeyData(ByteString.copyFromUtf8(VALID_KEY_DATA_2))
@@ -246,14 +283,13 @@ private TemporaryExposureKey createOutdatedKey() {
         .setTransmissionRiskLevel(5).build();
   }

-  private static Collection<TemporaryExposureKey> buildPayloadWithInvalidKey() {
-    return Stream.of(
-        buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2), 999))
-        .collect(Collectors.toCollection(ArrayList::new));
+  private SubmissionPayload buildPayloadWithInvalidKey() {
+    TemporaryExposureKey invalidKey = buildTemporaryExposureKey(VALID_KEY_DATA_1, createRollingStartIntervalNumber(2), 999);
+    return buildPayload(invalidKey);
   }

   private void assertElementsCorrespondToEachOther(Collection<TemporaryExposureKey> submittedTemporaryExposureKeys,
-      Collection<DiagnosisKey> savedDiagnosisKeys) {
+                                                   Collection<DiagnosisKey> savedDiagnosisKeys) {

     Set<DiagnosisKey> submittedDiagnosisKeys = submittedTemporaryExposureKeys.stream()
         .map(submittedDiagnosisKey -> DiagnosisKey.builder().fromProtoBuf(submittedDiagnosisKey).build())
diff --git a/services/submission/src/test/resources/application.yaml b/services/submission/src/test/resources/application.yaml
--- a/services/submission/src/test/resources/application.yaml
+++ b/services/submission/src/test/resources/application.yaml
@@ -23,6 +23,7 @@ services:
     retention-days: 14
     random-key-padding-multiplier: 10
     connection-pool-size: 200
+    maximum-request-size: 100KB
     payload:
       max-number-of-keys: 14
     verification:

Plausible Deniability: Add "Padding" property to submission payload
Currently, mobile apps are collecting keys based on their install date. When a user uploads his keys (after being tested positive), the payload will contain 1-13 keys. Although the traffic between mobile & server is secured, an attacker may still sniff the packages in the network and predict, based on the request size, how many keys are probably part of the submission request. This may then lead to additional information for the attacker in an attempt to deanonymize a user.

In order to mitigate this attack, add a new field to the submission payload, called `padding` of type string. The field shall be optional and must not break compatibility to the current/older mobile implementations (which should be given due to the use of protobuf). The server shall not process this field.