aws / aws/aws-lambda-rust-runtime

SNS `Timestamp` doesn't round-trip: serialization drops `.000` subseconds, corrupting the signed string-to-sign

Đang mở
#1,161 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Rust
Star
3.6k
Fork
396
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

The SNS event structs (`SnsMessage`, `SnsSubscriptionMessage`, `SnsMessageObj`) parse `Timestamp` into `chrono::DateTime`, and re-serializing does not reproduce the string SNS sent: chrono's default serializer uses `SecondsFormat::AutoSi`, which omits the subsecond part when it is zero.

```rust
let ts = "2019-01-02T12:45:07.000Z"; // Timestamp from the SNS sample event in the Lambda docs
let parsed: chrono::DateTime = serde_json::from_value(serde_json::json!(ts))?;
assert_eq!(serde_json::to_value(parsed)?, "2019-01-02T12:45:07Z"); // ".000" is gone
```

SNS itself always emits exactly three fractional digits, including `.000` on whole seconds. The [sample event in the Lambda developer guide](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html) shows `"Timestamp": "2019-01-02T12:45:07.000Z"`, and I confirmed the fixed three-digit format against a captured production payload whose signature verifies against the corresponding `SimpleNotificationService-*.pem` signing certificate (chained to Amazon Root CA 1).

## Impact

The SNS message signature covers the `Timestamp` string verbatim — the `signature` field docs on these structs say so. Anyone verifying SNS signatures on Lambda-delivered events who rebuilds the string-to-sign from these structs computes a canonical string that differs from what SNS signed whenever the message was published on a whole second. Roughly 1 in 1000 valid messages fails verification and gets dropped — silent, rare, and invisible in testing (any timestamp with nonzero milliseconds round-trips fine). I confirmed end to end with signed envelopes: a `.000Z` message verifies as delivered and fails after a round trip through `SnsMessage`; a `.719Z` message passes both ways.

Secondary effect: test events generated by serializing these structs (e.g. via the `builders` feature) carry timestamps in a format real SNS never produces.

## Proposed fix (non-breaking)

Pin serialization to SNS's actual format, leaving deserialization and the field type unchanged:

```rust
// custom_serde
pub fn serialize_rfc3339_millis(
dt: &DateTime,
s: S,
) -> Result {
s.serialize_str(&dt.to_rfc3339_opts(SecondsFormat::Millis, true))
}
```

```rust
#[serde(serialize_with = "crate::custom_serde::serialize_rfc3339_millis")]
pub timestamp: DateTime,
```

With this, every timestamp SNS actually produces round-trips byte-identically, so the reconstructed string-to-sign matches and generated fixtures match real payloads. I'm happy to send this PR.

## Alternative (breaking)

Preserve the raw string — `pub timestamp: String`, or a wrapper holding both the raw string and the parsed `DateTime`, serializing the raw string verbatim. This is the only variant that stays correct even if AWS ever changes its timestamp precision, but it breaks every consumer of the field (and diverges from `aws-lambda-go`, whose `SNSEntity` uses `time.Time` with the same limitation). Mentioning it for completeness; the non-breaking fix above covers the observed format.

Regardless of the fix, it may be worth a doc note on these fields that signature verification is best performed against the raw payload bytes — that is what AWS's official validators do, and it is immune to representation issues entirely.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu bằng cách xác định các struct sự kiện SNS SnsMessage, SnsSubscriptionMessage và SnsMessageObj, sau đó kiểm tra quá trình tuần tự hóa timestamp của chúng và module custom_serde. Kiểm tra các round trip với các ví dụ .000Z và .719Z được cung cấp; được xem là hoàn tất khi timestamp SNS giữ lại chính xác ba chữ số phần lẻ, để các chuỗi đã ký được tái tạo khớp với các chuỗi ban đầu.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
rust
Lĩnh vực
backend, cloud
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
68/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.