WebOfTrust / signify-browser-extension

Apache License 2.0
5 stars 6 forks source link

Review architecture with community members #19

Closed 2byrds closed 8 months ago

2byrds commented 9 months ago

Review the latest design with Daniel/Provenant, keri devs, etc. in order to make any final changes/adjustments/additions and update the diagrams, issues, etc.

rodolfomiranda commented 9 months ago

Link to slides

rodolfomiranda commented 9 months ago

A proposed flow of events:

  sequenceDiagram
  autonumber
    participant backend
    webpage->>backend: request resource
    backend->>webpage: request authentication (+ nonce)
    webpage->>content-script: request authentication
    content-script->>background: request authentication
    opt if locked
    background->>content-script: send locked state
    content-script->>content-script: show alert to unlock
    content-script->>background: send unlock request
    background->>background: display popup or side panel to unlock
    background->>keria: request AIDs/VCs
    keria->>background: send AIDs/VCs
    background->>background: rerieve AID asociation
    end
    opt if not AID asociation
    background->>background: display popup or side panel for AID/VC pickup
    background->>background: save AID asociation
    end
    background->>content-script: send AID/VC
    content-script->>content-script: show alert to confirm AID/VC
    content-script->>background: confirm AID/VC
    background->>background: generate signed auth data
    background->>content-script: send signed auth data
    content-script->>webpage: send signed auth data
    webpage->>backend: send signed auth data
    backend->>webpage: send authorized content
rodolfomiranda commented 9 months ago

A meeting was held between Daniel, Arsh, Lance and Rodolfo on Dec 15th.

SmithSamuelM commented 9 months ago

Not shown in diagram is replay attack protection on backend for signatures.

rodolfomiranda commented 9 months ago

I'll add explicitly that the request for authentication from the server must include a nonce

rodolfomiranda commented 9 months ago

nonce added. There's a pending architecture definition for authentication with no library dependency that will be followed in #23

SmithSamuelM commented 9 months ago

My suggestion would be to consider a non-interactive authentication mechanism with replay attack protection. Signing for authentication does not need to be interactive whereas the conventional web approach to authentication is to use a shared encrypttion key with an encrypted nonce for a challenge response that therefore must be interactive. When you sign you only need replay attack protection you don't need a challenge response. You can use a nonce for replay attack protection but you can also use a timestamp instead and it becomes asynchronous and non-interactive. This is the KRAM appraoch (see the KRAM hackmd https://hackmd.io/@SamuelMSmith/B1A5jlKXd?type=view)

SmithSamuelM commented 9 months ago

When you are using a shared encryption key for authentication you have no choice but for it to be interactive with a nonce but then you are doing session based authentication. But when signing you have a choice, and non-interactive replay attack protection enables asynchronous authentication per request instead of session based authentication. If you are using a nonce and not session based authentication then you incur the penalty of having to do interactive authentication per request not merely per session.

Signing should change how we think about authentication

rodolfomiranda commented 9 months ago

That's a great point. So seems that a monotonically increasing number or timestamp has the same security properties as a nonce. That may solve the other problem that we are facing in chrome extension that with discussed with @dhh1128. For security reasons, the latest manifest version v3 does not allow to modify headers on the fly. You now need to use a DeclarativeNetRequest API that is a way to define in advance what modifications will be placed in the next request. For example if you want to add a header with a signature in a request, you need to pre-define the header tag and value beforehand and the browser will handle the change when is needed. If we use a timestamp as part of the signed data, will be able to use the DeclarativeNetRequest API to modify any request from webpages. The timestamp will be monotonically increasing but with the time when the header is predefined and not the time of the network request. The server must validate the monotonicity requirement and may also add a time window of acceptance. The signed header will be kept by the browser and not the extension nor the webpage code. So security in that regards move to the security of Chrome, Firefox and Safari. I'd like to hear opinions on use of predefined headers with signatures. @SmithSamuelM, @dhh1128, @pfeairheller

pfeairheller commented 9 months ago

If the intent of this extension is to satisfy the requirements for vLEI pilots that we will be embarking on next year, this approach is insufficient. As a matter of fact, it is anticipated that web pages will need to be modified to work with a Signify powered browser extension. This is not guessing at a web page layout to insert a username and password as 1Password does. We need to sign every request with replay attack prevention and, much like adding support for Passkeys, we anticipate the participating websites will need to be modified.

Honestly, I'm struggling to understand how this could possibly work without modifications to the target web site. For example, an entire verification step and credential presentation step must be added for vLEI role credentials to be considered valid authentication mechanisms. With that requirement, it is not unreasonable to expect web apps to use a library that can interact with the plugin to sign every request correctly.

rodolfomiranda commented 9 months ago

We are thinking of having two approaches:

pfeairheller commented 9 months ago

I think it is worth further conversation around the need for #2 above. In order to verify a new type of signature in the headers of requests from a web app, the backend web server will have to be modified. If that is the case, why is it necessary to provide an option that ONLY modifies the back end and not the webapp as well?

dhh1128 commented 9 months ago

@pfeairheller : I am interested in approach 2. I would like to discuss.

SmithSamuelM commented 9 months ago

. @rodolfomiranda

So seems that a monotonically increasing number or timestamp has the same security properties as a nonce

No no. Only a timestamp that is relative to the clock of the server not the client making the request. A seqence number won’t work, is impractical because the client has no way of boostrapping without an interaction whereas a timestamp is universal quantity that only differs by clock drift which is what the window accounts for. Seems like you have some requirement I don’t understand the purpose of with respect to pre-defined headers. This stack overflow shows you how to modify headers for plugins with Manifest V3. https://stackoverflow.com/questions/3274144/can-i-modify-outgoing-request-headers-with-a-chrome-extension

psteniusubi commented 9 months ago

For replay protection tokens must have a concept of audience, to prevent replaying tokens across backends. Otherwise it could be possible for a malicious backend to replay tokens it has received at some other backend. If Http Message Signing is used to generate tokens, then this could be addressed by including the Host header in Signature-Input and make sure the backend accepts only known Host values

psteniusubi commented 9 months ago

About architecture. I think we have two quite different main use cases

  1. Presenting vLEI credentials
  2. Using HTTP message signatures to authenticate individual HTTP requests

In my opinion the first main use case is by far the most valuable.

For Presenting vLEI credentials there are further use cases

Both of these credential presentation use cases could be implemented with a WebAuthn style JavaScript API. An other alternative is that the browser extension detects and knows how to respond to a OIDC SIOP style authentication challenge.

The second main use case is less valuable. Practically all web applications already implement the concept of an authenticated session. The session could be based on HTTP cookies, access tokens, JWT tokens, API keys or some other existing technology. In most cases this works reasonable well. For adoption of this solution I don't think it's a good idea to begin with suggesting replacing such fundamental concepts.

rodolfomiranda commented 9 months ago

@SmithSamuelM

This stack overflow shows you how to modify headers for plugins with Manifest V3. https://stackoverflow.com/questions/3274144/can-i-modify-outgoing-request-headers-with-a-chrome-extension

That answer shows how to use the DeclarativeNetRequest API that I mentioned. Note that you need to create or update the "rules" before the network request happens, so the header value must be precomputed and inserted in that rule update. In our case that means that we can't create a header with a signature of the timestamp of the ongoing request because we do not know when the request will be triggered. We can only create the signature at the time of updating the rule. For example updating the rule with a new signature after every network request that will be used by the browser to modify the headers in the next request.

From Chrome docs: "Manifest V3 changes how extensions handle modification of network requests. Instead of intercepting network requests and altering them at runtime with chrome.webRequest, your extension specifies rules that describe actions to perform when a given set of conditions is met"

dhh1128 commented 9 months ago

I just want to double down on @rodolfomiranda's last comment. As I understand it, in v3, an extension can no longer see a request and its headers, and modify them, directly. Basically, browsers are suspicious of extensions (with good cause), and are locking down more and more what an extension can see and do. So what the extension does is say, "The next request needs to have the following header (name, value) inserted." The browser then inserts the header whenever the next request is made, and the extension has no control of when that happens. Since the part of the client that knows KERI (the extension) is not the part that can set the headers directly, this puts a genuine wrinkle into the straightforward mental model of KRAM, which assumes the client can set headers on its own requests. KRAM is usable in CURL or the python requests library, but we need to get creative to use it in a browser extension. It can be done, but it requires the sort of workaround Rodolfo is proposing.

dhh1128 commented 9 months ago

why is it necessary to provide an option that ONLY modifies the back end and not the webapp as well?

Not all websites are webapps. The spec for this extension called for it to work with HTML5, not just with webapps, and to follow the pattern of HTTP authentication schemes. HTTP authentication schemes are usable with static HTML.

Of course we need KERI-aware code on the client side, or else signatures would be impossible. But we are putting KERI-aware code into the extension. If we say, "Yes, we need it there, but we ALSO need it in the webapp," we are significantly changing the adoptability of the mechanism. Now a website administrator can't turn on KERI-based authentication just by configuring the back end and assuming that users will have an extension; he also has to get programmers to rewrite the front end. That is extremely regrettable, IMO, and I wish it could be avoided.

If I absolutely CANNOT avoid it, then there are still two different levels of imposition on programmers, and I wish to distinguish between them because they have radically different adoptability:

  1. Require programmers to add a <script> tag to every page, introducing a dependency and lib that automagically works with the extension and a KERI-enabled backend. No links have to be rewritten, and the developer has no learning curve and no interest in what's going on.

  2. Require programmers to learn how, under what conditions, with what arguments, and with what caveats to use a javascript library in every feature of their webapp.

Obviously, option 2 is way more painful than option 1. If a website is just trying to get simple authentication (the EBA case), I think option 2 is unreasonable. On the other hand, if a website is trying to do lots of sophisticated cryptographic operations, option 2 is fine.

I think 99.9% of websites fall just want simple authentcation, and should not be required to go through the learning curve of option 2.

SmithSamuelM commented 8 months ago

@rodolfomiranda @dhh1128 It appears from a little more reading that Google is pushing everyone to use their identity API for authenticating requests in extensions which means using OIDC and bearer tokens. A way to work around this is to to treat the extension as a signing library and send messages back and forth between the web app and the extension. This may allow the web app to manage the headers. The extension and the remote host will likely need to see that the signatures are working. But for request authentication with replay, the web app asks the extension to sign and then sends the signed request instead of the extension intercepting the signed request.

https://developer.chrome.com/docs/extensions/develop/concepts/messaging

SmithSamuelM commented 8 months ago

as long as the private keys are only in the extension, the worst an attacker can do is ddos the web app by not allowing it to communicate with the extension. the extension can still prompt to the user what it is being asked to sign and get user approval before signing.

rodolfomiranda commented 8 months ago

@SmithSamuelM , a follow up question regarding the KRAM non interactive authentication. In you paper you stated that a redirection of the request may be detectable

because either the requestee does not get the response at all if it is not forwarded or it gets it via a later redirection from some host that is not the originating requestee

I'm imagining the following scenario where that detection seems not possible and I'm having difficulty to find a solution using the non interactive authentication: Suppose that I can use the non interactive method with timestamp from my home-banking page to retrieve my account balance. Also, suppose that I use the same non interactive method from the "cat of the day" web page to get some funny cat pictures. In that scenario, the "cat of the day" server can redirect my authentication to the home banking within a milisecond time-frame and get my bank account balance. I won't be able to detect it because I still get a response back from the cat-of-the-day server. The malicious page can also get many authenticated request with the monotonic increasing timestamp and fool the home-banking in subsequent request. Is it possible to tackle that problem with the non interactive auth method?

dhh1128 commented 8 months ago

If the signature includes the URL of the request, doesn't that eliminate this attack?

On Wed, Dec 27, 2023, 7:32 AM Rodolfo @.***> wrote:

@SmithSamuelM https://github.com/SmithSamuelM , a follow up question regarding the KRAM non interactive authentication. In you paper you stated that a redirection of the request may be detectable

because either the requestee does not get the response at all if it is not forwarded or it gets it via a later redirection from some host that is not the originating requestee

I'm imagining the following scenario where that detection seems not possible and I'm having difficulty to find a solution using the non interactive authentication: Suppose that I can use the non interactive method with timestamp from my home-banking page to retrieve my account balance. Also, suppose that I use the same non interactive method from the "cat of the day" web page to get some funny cat pictures. In that scenario, the "cat of the day" server can redirect my authentication to the home banking within a milisecond time-frame and get my bank account balance. I won't be able to detect it because I still get a response back from the cat-of-the-day server. The malicious page can also get many authenticated request with the monotonic increasing timestamp and fool the home-banking in subsequent request. Is it possible to tackle that problem with the non interactive auth method?

— Reply to this email directly, view it on GitHub https://github.com/WebOfTrust/signify-browser-extension/issues/19#issuecomment-1870359338, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQ3JCBAH7BSOACPQYBUAADYLQWOLAVCNFSM6AAAAABAQEXIRWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNZQGM2TSMZTHA . You are receiving this because you were mentioned.Message ID: @.***>

rodolfomiranda commented 8 months ago

yes, that's the only practical solution I see so far, but you rely on DNS.

psteniusubi commented 8 months ago

In my comment above I was calling this attack replay across backends.

The OIDC and SAML style mitigation to this is to include audience in the generated tokens. Signing the complete URL with @target-uri is one possible solution. Signing the Host header with @authority is an other.

The WebAuthn style solution is to generate a new identifier on each web site identified by Origin. This solution is both phishing resistant and privacy preserving.

psteniusubi commented 8 months ago

How does the extension handle requests initiated by scripts and resources on a html page? For example fetch, img, iframe etc.

Cross-origin requests in particular is an important scenario to consider.

rodolfomiranda commented 8 months ago

How does the extension handle requests initiated by scripts and resources on a html page? For example fetch, img, iframe etc.

for the first approach in https://github.com/WebOfTrust/signify-browser-extension/issues/19#issuecomment-1863460674 , the page scripts should get signed headers from the browser extension and insert them in the requests. A library can be provided to simplify the process.

Cross-origin requests in particular is an important scenario to consider. That's not clear for me. For same origin request we can get the web page url and include in the signature. For cross-origin we may need to add the origin web page url and the requested resource url.

psteniusubi commented 8 months ago

for the first approach in #19 (comment) , the page scripts should get signed headers from the browser extension and insert them in the requests. A library can be provided to simplify the process.

Isn't this quite risky? As an attacker I don't even need to replay any signature tokens from the backend. Following your example I'll setup the "cat of the day" page so it simply runs javascript fetch to retrieve your balance from your your home banking backend.

To mitigate this the extension would have to respect any relevant CORS parameters, attributes, headers etc.

A WebAuthn style approach where a unique identifier is generated for each web site sounds more feasible. In this case any requests originating from the "cat of the day" page will be signed with a unique identifier that will be unknown to the home banking backend.

rodolfomiranda commented 8 months ago

The browser extension is not allowed to modify request on the fly (blocking). What it can do is to provide the signatures to the webpage. Following the a non-interactive authentication approach, the extension can sign the timestamp and the URL of the webpage since it can get that from the browser and not from the webpage itself. That will prevent the replay/forwarding attack to the bank backend.

pfeairheller commented 8 months ago

The browser extension is not allowed to modify request on the fly (blocking). What it can do is to provide the signatures to the webpage. Following the a non-interactive authentication approach, the extension can sign the timestamp and the URL of the webpage since it can get that from the browser and not from the webpage itself. That will prevent the replay/forwarding attack to the bank backend.

Following along with this example, what protection is there against a man-in-the-middle interception a POST to your bank where you want to transfer $10 and they change the body of the POST to transfer $1000 instead? That will not be detectable by the bank's backend.

rodolfomiranda commented 8 months ago

Do you mean a man in the middle between the bank and the home banking page, like hacked DNS, or a man in the middle between the home banking page and the browser extension, or something else? In the first case the content is encrypted by TLS, so headers are no exposed unencrypted. I think that the m-i-m attack should hack the DNS and then will be able to fake everything. The second case may happens if your browser is compromised, with a malicious extension or a virus in your OS for example, so again they can have full control of any web page. In those two cases we are out of the game. Were you thinking in a different case?

pfeairheller commented 8 months ago

Between your browser and the bank backend. I thought we were trying to do better than rely on DNS/TLS?

rodolfomiranda commented 8 months ago

Between your browser and the bank backend. I thought we were trying to do better than rely on DNS/TLS?

Yes, that's the goal. That's why I asked https://github.com/WebOfTrust/signify-browser-extension/issues/19#issuecomment-1870359338 with the intention to explore ideas.

SmithSamuelM commented 8 months ago

@rodolfomiranda the full request is signed. If its a get without a body the query parameters are signed. If its a post the post body is signed.. So we embed a UUID salty nonce or SAID in the request. This is generated by the client so its non-interactive. if that request has a uuid embedded in it then a request can be treated as idempotent by the server and a replay by cat in the hat is detected. If the request is signed then the signature itself counts as a UUID for the purpose of replay attack detection. signatures are also asymmetric digests.

If you read KRAM carefully it talks about different levels of replay detection. A simple timestamp with a window is good for ddos protection for low risk queries but for high risk queries then the server caches the requests and can do explicit matching so there is zero replay possoble within the time window. And the window means that replays outside the time window are also not possible. Then the server can make such high risk requests idempotent and a replay of that request via cache poisoning of DNS just redirects it doesn’t change the request itself. What is missing is that the client may also need to authenticate the server. An interactive nonce generated by a redirected server does not authenticate the server. This type of MITM i.e. to an unauthenticated server still works interactive or not.

The only way to reliably fix that in a purely noninteractive way is to send the message asymmetricaly encrypted to the server’s authenticated private decryption key. Then a redirected MITM can’t read the request but can only replay it. And if the true server ensures that high risk requests (such as spending money) are idempotent then the redirected replay will fail.

Alternatively for high value requests the server can escalate to an interactive protocol by sending a confirmation response with a UUID that says to the client. Are you sure you want to do this. The response is signed by the server. So now the server is authenticated. The client then signs and returns the confirmation. The whole transaction must be idempotent. The difference here is that the interaction is signed both ways not merely using an unsigned nonce which does not authenticate the server. So its both replay attack protected (via idempotency) and authenticates both sides client and server using signatures not shared secrets.

Authentication bothys wasy happens when the client signs the request and either uses:

  1. Assymetric encryption (HPKE) to ensure that only the authentic server can understand the request.
  2. an interactive exchange that requires the server to sign its confirmation response.

Idempotency is how the authentic server ensures that the replay is detected. Either idempotency of the request or idempotency of the whole interactive transaction. A timestamp in a window is a low risk way to have weak idempotency which is good enough for low value requests to protect against replay attack. For really high value requests then the idempotency must be strong. All that requires that each request from the client be unique informationally and have some type of tamper evidence. Since attaching a digest can be down by any MITH, we can’t use a digest. It has to be a protected digest so a signature from an authenticated key pair works. And we get client authentication for free.

So if you are going to sign then you already can do idempotency either by using the signature as the UUID or by embedding a UUID and then signing that.

But there is a trade-off because idempotency requires more resources. so omly use it on high value requests and use the cheaper replay attack protection on low value requests. And if you need to authenticate the server to the client then use either asymmetric encryption from client to server or use an interactive exchange of signed messages or both.

This is one reason why SPAC uses HPKE fir asymnmetric encryption. Because plain symmetric encryption that exchanges a shared secret encryption key is subject to a dns redirection attack where the symmetric key exchange happens with a MITM who has a forged but verifiable certificate. And SPAC also uses ESSR i.e. signs the request becasue HPKE by itself is not protected from key compromise impersonation attacks. So we do both, signed asymmetric encryption ESSR which means we can send messages across public infrastructure in a non-interactive way and be protected from MITM attacks. The recepient must still do idempotency checking to prevent replay attacks but a signed message is inherently idempotent detectable and if it has a timestamp then the size of the idempotent detecting cache becomes manageable.

SmithSamuelM commented 8 months ago

My assumption in suggesting the low level simple KRAM was that the signify client was only being used to manage its KERIA which agent is only managing AIDs and ACDC issuance. the associated management operations are all idempotent so the worst that happens if a replay happens within a time window is some limited wasted compute time. When you expand the class of actions of the client to be something like fungible high value transactions like banking then you need to use the high level KRAM which means non-interactive replay is itself idempotent so that the carried transaction is protected even when it is not itself idempotent.

SmithSamuelM commented 8 months ago

If the host is performing non-idempotent tasks or is requesting information that is non verifiable that needs to be authenticated both ways at the request level (not the information level) and the client can’t trust DNS/CA to authenticate the host. Then the choices get smaller. 1) use an interactive salty nonce exchange that is signed both ways or some other interaction that is signed both ways. 2) use a non-interactive signed request but with asymmetric encryption to the host.

But the above requirement seems to be out of scope for the purpose of submitting reports or interacting with Keria. So I am confused. Maybe I am not being helpful.

SmithSamuelM commented 8 months ago

DNS/CA TLS does two things. Provides and encrypted channel so third parties can’t view the contents. 2) provides weak authentication of the host to the client. If the client also uses a certificate then DNS/CA TLS can also provide weak authentication of the client to the host.

When I say weak authentication it is because TLS does authentication using a shared secret exchanged with encryption public/private key pairs not signing key pairs. When the client signs requests using KERI then the client is strongly authenticated to the Host but the host may be subject to replay attacks of those signed requests. Replay attack protection of the clients requests does not solve the weak authentication of the host. If the host signs everything it sends to the client using its KERI keys then we have strong authentication both ways but still subject to replay attack. The question then becomes, how strong does the replay attack protection need to be for a given exchange either a request or a response or any set of requests and responses?

If the client uses TLS to establish a connection with the host, the encryption is strong. The major attack mode on authenticity of the host are a hijack or a key compromise impersonation attack. In the first that the client thinks it is talking to the true host but is not. There may be a MITM undetectable by the client. If all messages sent between the client and host are signed by each side, then the MITM can’t inject malicious traffic that is verifiable. It can only replay or drop. Assuming that the connection is MITM means answering the question, what types of messages need replay attack protection and how tight. Does every message both ways need to use an interactive challenge response with a salty nonce, or is a non-interactive monotonically increasing time stamp good enough. The answer is a function of the type of message.

rodolfomiranda commented 8 months ago

Thanks Sam for the detailed answer, it's really helpful. The initial scope of the extension is to authenticate in a webpage and be able to submit a report. For that case, signing a timestamp, the webpage origin url, and may be the body content seems enough.

In addition, the extension could be used for other purposes that may impose stricter protection as you outlined. That will require to agree and define an API between the web page and the browser extension that can handle the requirements.

rodolfomiranda commented 8 months ago

Follow discussion on automated signed headers in #23