Unsigned API responses #1427

Open
opened 2025-12-28 17:22:57 +00:00 by sami · 10 comments
Owner

Originally created by @cthulhu-rider on GitHub (Jun 11, 2025).

each NeoFS response has nspcc-dev/neofs-api@797e8303ff/container/service.proto (L140) field. It carries crypto signatures of the response payload and meta header. If the response message route consists of several API servers, each one adds its own signature

the purpose of this approach is:

  1. signature ensures response data integrity
  2. public key authenticates the server
  3. client can track the request route

but:

  • extra CPU, RAM and net capacity are spent on calculating and sending signature
  • it does not protect from replay and other attacks
  • the route is visible but not verifiable. And even if it were so, hardly anyone would be interested in it
  • the listed advantages are implemented by protocols of other network levels (TLS)

based on these facts, NeoFS API protocol can be simplified by eliminating all response verification headers

Describe the solution you'd like

  1. deprecate the field in all response messages
  2. keep signing responses for client requests with meta_header.version <= v2.17
  3. wait one release/update cycle, then prohibit the field and never sign responses

Describe alternatives you've considered

no

Additional context

Originally created by @cthulhu-rider on GitHub (Jun 11, 2025). ## Is your feature request related to a problem? Please describe. each NeoFS response has https://github.com/nspcc-dev/neofs-api/blob/797e8303ff7a8b7fb55af57d0f10489140f8053c/container/service.proto#L140 field. It carries crypto signatures of the response payload and meta header. If the response message route consists of several API servers, each one adds its own signature the purpose of this approach is: 1. signature ensures response data integrity 2. public key authenticates the server 3. client can track the request route but: * extra CPU, RAM and net capacity are spent on calculating and sending signature * it does not protect from replay and other attacks * the route is visible but not verifiable. And even if it were so, hardly anyone would be interested in it * the listed advantages are implemented by protocols of other network levels (TLS) based on these facts, NeoFS API protocol can be simplified by eliminating all response verification headers ## Describe the solution you'd like 1. deprecate the field in all response messages 2. keep signing responses for client requests with `meta_header.version <= v2.17` 3. wait one release/update cycle, then prohibit the field and never sign responses ## Describe alternatives you've considered no ## Additional context * performance tests * https://github.com/nspcc-dev/neofs-sdk-go/issues/661
Author
Owner

@roman-khimov commented on GitHub (Jun 11, 2025):

  1. https://github.com/neo-project/neofs-api-csharp is likely to break. Who is to fix it?
  2. We can't just drop all signatures. The most simple cases are GET or HEAD where we can verify the result irrespective of additional signatures. But other cases are not that easy, unless we're TLS there is no way to say the response is authentic. But, at the same time, we can say that it's the same for JSON-RPC for example. So some thought should be put at it as well as some additional tests. Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET?
@roman-khimov commented on GitHub (Jun 11, 2025): 1. https://github.com/neo-project/neofs-api-csharp is likely to break. Who is to fix it? 2. We can't just drop all signatures. The most simple cases are GET or HEAD where we can verify the result irrespective of additional signatures. But other cases are not that easy, unless we're TLS there is no way to say the response is authentic. But, at the same time, we can say that it's the same for JSON-RPC for example. So some thought should be put at it as well as some additional tests. Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET?
Author
Owner

@cthulhu-rider commented on GitHub (Jun 12, 2025):

this gonna be a protocol change. Requests with older versions will be served with signing

unless we're TLS there is no way to say the response is authentic

that's exactly what TLS was created for i think, I'd rely on it

Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET?

i tested this GET(V2) API nspcc-dev/neofs-api@e25f79d69d only. Maybe I'll find time to make a comparison b/w two granular changes. If so, I'll write about it here

@cthulhu-rider commented on GitHub (Jun 12, 2025): this gonna be a protocol change. Requests with older versions will be served with signing > unless we're TLS there is no way to say the response is authentic that's exactly what TLS was created for i think, I'd rely on it > Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET? i tested this GET(V2) API https://github.com/nspcc-dev/neofs-api/commit/e25f79d69dafc6a1067e7fd8953db69fdd060bea only. Maybe I'll find time to make a comparison b/w two granular changes. If so, I'll write about it here
Author
Owner

@roman-khimov commented on GitHub (Jun 15, 2025):

My thought was to make it configurable. At least it's safer for now. Then it could be made the default, especially if we have protocol version in requests.

@roman-khimov commented on GitHub (Jun 15, 2025): My thought was to make it configurable. At least it's safer for now. Then it could be made the default, especially if we have protocol version in requests.
Author
Owner

@cthulhu-rider commented on GitHub (Jun 17, 2025):

Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET?

here are results i've got on S3 tests using Warp:

Test Master New GET Unsigned GET
Size=4K Threads=10 1575 1687 1721
Size=4K Threads=100 2927 3326 3584
Size=4M Threads=10 206 200 449
Size=4M Threads=100 480 472 652
@cthulhu-rider commented on GitHub (Jun 17, 2025): > Take new-get test for example, how much of its gains are from signature eliminations and how much are from new GET? here are results i've got on S3 tests using Warp: Test | Master | New GET | Unsigned GET -- | -- | -- | -- Size=4K Threads=10 | 1575 | 1687 | 1721 Size=4K Threads=100 | 2927 | 3326 | 3584 Size=4M Threads=10 | 206 | 200 | 449 Size=4M Threads=100 | 480 | 472 | 652
Author
Owner

@roman-khimov commented on GitHub (Jun 17, 2025):

New+Unsigned? It seems like New is ~useless. But given that signatures prevail here, maybe it gives more meaningful difference when they're not present.

@roman-khimov commented on GitHub (Jun 17, 2025): New+Unsigned? It seems like New is ~useless. But given that signatures prevail here, maybe it gives more meaningful difference when they're not present.
Author
Owner

@cthulhu-rider commented on GitHub (Jun 18, 2025):

@roman-khimov here's what i've got

Test New GET Unsigned GET Both Unsigned + checksum
Size=4K Threads=10 1587 1826 1861 1764
Size=4K Threads=100 3091 3541 3602 3334
Size=4M Threads=10 195 412 413 366
Size=4M Threads=100 475 703 700 705

dropping signatures has a significant impact

chunk transmission in heading message affects much less. But still this is a nice enhancement

in total, i'd make both improvements based on the request API version

@cthulhu-rider commented on GitHub (Jun 18, 2025): @roman-khimov here's what i've got Test | New GET | Unsigned GET | Both | Unsigned + checksum -- | -- | -- | -- | -- Size=4K Threads=10 | 1587 | 1826 | 1861 | 1764 Size=4K Threads=100 | 3091 | 3541 | 3602 | 3334 Size=4M Threads=10 | 195 | 412 | 413 | 366 Size=4M Threads=100 | 475 | 703 | 700 | 705 --- dropping signatures has a significant impact chunk transmission in heading message affects much less. But still this is a nice enhancement in total, i'd make both improvements based on the request API version
Author
Owner

@carpawell commented on GitHub (Jun 18, 2025):

But it feels like New's improvement is kinda test error, it does not differ much more than 1%

@carpawell commented on GitHub (Jun 18, 2025): But it feels like New's improvement is kinda test error, it does not differ much more than 1%
Author
Owner

@cthulhu-rider commented on GitHub (Jun 18, 2025):

it's pretty visible in 4K runs

@cthulhu-rider commented on GitHub (Jun 18, 2025): it's pretty visible in 4K runs
Author
Owner

@carpawell commented on GitHub (Aug 15, 2025):

it's pretty visible in 4K runs

I looked at both tables: Master from the first one and New GET from the second one, it is 1575 vs 1587 and 2927 vs 3091. Also values in New GET vary in tables from 1687 to 1587 so i guess the improvement cannot be proved with this result (i am considering this as an API change, so i would apply such a change only if its improvement can be easily felt)

@carpawell commented on GitHub (Aug 15, 2025): > it's pretty visible in 4K runs I looked at both tables: `Master` from the first one and `New GET` from the second one, it is `1575` vs `1587` and `2927` vs `3091`. Also values in `New GET` vary in tables from `1687` to `1587` so i guess the improvement cannot be proved with this result (i am considering this as an API change, so i would apply such a change only if its improvement can be easily felt)
Author
Owner

@roman-khimov commented on GitHub (Dec 22, 2025):

I think we're ready to do this for all responses. We don't sign HEAD/GET responses since https://github.com/nspcc-dev/neofs-api/pull/334. What we have left is:

  • Put, but it's confirmed by the object ID returned, that's the only thing client cares about in fact
  • Delete, same as above with tombstone ID
  • Search/SearchV2, there we're assembling a final response from bits and pieces other nodes send to us, garbage in -> garbage out, irrespective of final node signature, "ttl 1" responses are also impossible to prove right or wrong, it only confirms authenticity, but that's what TLS provides even better
  • GetRange/GetRangeHash, same as Search/SearchV2
  • Replicate just has its own rules, not relevant
@roman-khimov commented on GitHub (Dec 22, 2025): I think we're ready to do this for all responses. We don't sign HEAD/GET responses since https://github.com/nspcc-dev/neofs-api/pull/334. What we have left is: * Put, but it's confirmed by the object ID returned, that's the only thing client cares about in fact * Delete, same as above with tombstone ID * Search/SearchV2, there we're assembling a final response from bits and pieces other nodes send to us, garbage in -> garbage out, irrespective of final node signature, "ttl 1" responses are also impossible to prove right or wrong, it only confirms authenticity, but that's what TLS provides even better * GetRange/GetRangeHash, same as Search/SearchV2 * Replicate just has its own rules, not relevant
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nspcc-dev/neofs-node#1427
No description provided.