{"id":13409028,"url":"https://github.com/chmike/securecookie","last_synced_at":"2026-01-06T06:56:30.042Z","repository":{"id":43331811,"uuid":"102271088","full_name":"chmike/securecookie","owner":"chmike","description":"Fast, secure and efficient secure cookie encoder/decoder ","archived":false,"fork":false,"pushed_at":"2023-02-18T15:24:33.000Z","size":223,"stargazers_count":77,"open_issues_count":1,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-07-31T20:32:43.284Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chmike.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-09-03T14:40:29.000Z","updated_at":"2024-04-09T01:07:24.000Z","dependencies_parsed_at":"2024-01-08T14:30:44.689Z","dependency_job_id":"6c81051d-db3d-4cc6-8e3d-fdd5a90f1ff4","html_url":"https://github.com/chmike/securecookie","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmike%2Fsecurecookie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmike%2Fsecurecookie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmike%2Fsecurecookie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmike%2Fsecurecookie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chmike","download_url":"https://codeload.github.com/chmike/securecookie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243593264,"owners_count":20316157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:00:57.416Z","updated_at":"2026-01-06T06:56:30.023Z","avatar_url":"https://github.com/chmike.png","language":"Go","readme":"[![GoDoc](https://img.shields.io/badge/go.dev-reference-blue)](https://pkg.go.dev/github.com/chmike/securecookie)\n[![Build](https://travis-ci.org/chmike/securecookie.svg?branch=master)](https://travis-ci.org/chmike/securecookie?branch=master)\n[![Coverage](https://coveralls.io/repos/github/chmike/securecookie/badge.svg?branch=master)](https://coveralls.io/github/chmike/securecookie?branch=master)\n[![Go Report](https://goreportcard.com/badge/github.com/chmike/securecookie)](https://goreportcard.com/report/github.com/chmike/securecookie)\n![Status](https://img.shields.io/badge/status-stable-brightgreen.svg)\n![release](https://img.shields.io/github/release/chmike/securecookie.svg)\n\n# Encode and Decode secure cookies\n\nThis package provides functions to encode and decode secure cookie values.\n\nA secure cookie has its value ciphered and signed with a message authentication\ncode. This prevents the remote cookie owner from knowing what information is stored \nin the cookie or modifying it. It also prevents an attacker from forging a fake \ncookie.\n\nThis package differs from the Gorilla secure cookie in that its encoding and\ndecoding is 3 times faster and needs no heap allocation with an equivalent\nsecurity strength. Both use AES128 and SHA256 to secure the value.\n\n**Note:** This package uses its own secure cookie value encoding. It is thus\nincompatible with the Gorilla secure cookie package and the ones provided with other\nlanguage frameworks. This encoding is simpler and more efficient, and adds a\nversion number to support evolution with backwards compatibility.\n\n**Warning:** Because this package impacts security of web applications,\nit is a critical functionality. Review feedbacks are always welcome.\n\nIf you enjoy the code and want to thank me 🙏, you can [buy me a coffee](https://www.buymeacoffee.com/Chmike).\n\n## Content\n\n- [Installation](#installation)\n- [Usage examples](#usage-examples)\n- [Benchmarking](#benchmarking)\n- [Qualitative comparison](#qualitative-comparison)\n- [Value encoding](#value-encoding)\n- [Usage advise](#usage-advise)\n\n## Installation\n\nTo install or update this secure cookie package use the instruction:\n\n``` Bash\ngo get -u \"github.com/chmike/securecookie\"\n```\n\n## Usage examples\n\nTo use this cookie package in your server, add the following import.\n\n``` Go\nimport \"github.com/chmike/securecookie\"\n```\n\n### Generating a random key\n\nIt is strongly recommended to generate the random key with the following function.\nSave the key in a file using `hex.EncodeToString()` and restrict access to that file.\n\n``` Go\nvar key []byte = securecookie.MustGenerateRandomKey()\n```\n\nTo mitigate the risk of an attacker getting the saved key, you might store a second\nkey in another place and use the xor of both keys as secure cookie key. The attacker\nwill have to get both keys to reconstruct the effective key which should be more \ndifficult.\n\n### Instantiating a cookie object\n\nA secure cookie is instantiated with the `New` function. It returns an error if an\nargument is invalid.\n\n``` Go\nobj, err := securecookie.New(\"session\", key, securecookie.Params{\n\t\tPath:     \"/sec\",           // cookie received only when URL starts with this path\n\t\tDomain:   \"example.com\",    // cookie received only when URL domain matches this one\n\t\tMaxAge:   3600,             // cookie becomes invalid 3600 seconds after it is set\n\t\tHTTPOnly: true,             // disallow access by remote javascript code \n\t\tSecure:   true,             // cookie received only with HTTPS, never with HTTP\n\t\tSameSite: securecookie.Lax, // cookie received with same or sub-domain names\n})\nif err != nil {\n    // ...\n}\n```\n\nIt is also possible to instantiate a secure cookie object without returning an\nerror and panic if an argument is invalid. To do this, use\n`securecookie.MustNew()`. In the following example, `session` is the cookie name\nand the Path is `/sec`. A secured value may be stored in the remote browser by\ncalling the `SetValue()` method. After that, every subsequent request from that\nbrowser with a URL starting with `/sec` will have the cookie sent along. Calling\nthe method `GetValue()` will extract the secure value from the request. A\nrequest to delete the cookie may be sent to the remote browser by calling the\nmethod `Delete()`.\n\n``` Go\nvar obj = securecookie.MustNew(\"Auth\", key, securecookie.Params{\n\t\tPath:     \"/sec\",           // cookie received only when URL starts with this path\n\t\tDomain:   \"example.com\",    // cookie received only when URL domain matches this one\n\t\tMaxAge:   3600,             // cookie becomes invalid 3600 seconds after it is set\n\t\tHTTPOnly: true,             // disallow access by remote javascript code \n\t\tSecure:   true,             // cookie received only with HTTPS, never with HTTP\n\t\tSameSite: securecookie.Lax, // cookie received with same or sub-domain names\n}\n```\n\nRemember that the key should not be stored in the source code or in a repository.\n\n### Adding a secure cookie to a server response\n\n``` Go\nvar val = []byte(\"some value\")\n// with w as the http.ResponseWriter\nif err := obj.SetValue(w, val); err != nil {\n    // ...\n}\n```\n\n### Decoding a secure cookie value\n\nThe value is appended to the given buffer. If buf is `nil` a new buffer\n(`[]byte`) is allocated. If buf is too small it is grown.\n\n``` Go\n// with r as the *http.Request\nval, err := obj.GetValue(buf, r) \nif err != nil {\n  // ...\n}\n```\n\nThe returned value is of type []byte.\n\n### Deleting a cookie\n\n``` Go\n// with w as the http.ResponseWriter\nif err := obj.Delete(w); err != nil {\n  // ...\n}\n```\n\n**Note:** don't rely on the assumption that the remote user agent (browser) will\neffectively delete the cookie. Evil users will try anything to break your site.\n\n### Complete example\n\nAn complete example is provided [here](example/main.go).\n\nFollow these steps to test the example:\n\n1. create a directory in /tmp and set it as the working directory: \"mkdir /tmp/sctest; cd /tmp/sctest\"\n2. create a file named `main.go` and copy the example code referenced above into it\n3. create a go.mod file: \"go mod init example.com\"\n4. get the latest version of the securecookie packaqe: \"go get github.com/chmike/securecookie@v1.2.0\"\n5. run the server: \"go run main.go\"\n6. with your browser, request \"http://localhost:8080/set/someValue\" to set the secure cookie value\n7. with your browser, request \"http://localhost:8080/val\" to retriev the secure cookie value\n\n## Benchmarking\n\nEncoding the cookie named \"test\" with value \"some value\". See benchmark functions\nare at the bottom of cookie_test.go file. The ns/op values were obtained by running\nthe benchmark 10 times and taking the minimal value. These values were obtained with\ngo1.14 (27-Feb-2020) on an Ubuntu OS with an i5-7400 3GHz processor. \n\n|                |   Chmike |  Gorilla |\n| -------------: | -------: | -------: |\n|      Value len |       84 |      112 |\n|      Set ns/op |     2393 |     6309 |\n|      Get ns/op |     1515 |     5199 |\n|       Set B/op |      350 |     2994 |\n|       Get B/op |      192 |     2720 |\n|  Set allocs/op |        3 |       35 |\n|  Get allocs/op |        2 |       38 |\n\nThe secure cookie value encoding and decoding functions of this package need 0 \nheap allocations. \n\nThe benchmarks were obtained with release v0.4. Subsequent release may alter the benchmark results.\n\n## Qualitative comparison\n\nThe latest version was updated to put the security in line with the Gorilla\nsecure cookie.\n\n- We both use CTR-AES-128 encryption with a 16 byte nonce, and HMAC-SHA-256.\n- We both encrypt first then compute the MAC over the cipher text.\n- A time stamp is added to the encoded value.\n- The hmac is computed over the cookie value name, the ciphered time stamp and\n  value.\n- Both packages don't take special measures to secure the secret key.\n- Both packages don't effectively conceal the value byte length.\n\nThe differences between the Gorilla secure cookie and this implementation are:\n\n- This code is more efficient, and there is still room for improvement.\n- This secure value encoding is more compact without weakening the security.\n- This secure cookie encoding is incompatible with other secure cookie encoding.\n  I don't know the status of Gorilla's encoding.\n- This encoding adds an encoding version number allowing to change or add new\n  encoding without breaking backwards compatibility. Gorilla doesn't have this.\n- This package provides a Delete cookie method.\n\nThis package and Gorilla both provide equivalently secure cookies if we discard\nthe fact that no special measure is taken to conceal the key in memory and the\nvalue length. This package is quite new and needs more reviews to validate the\nsecurity of the implementation.\n\nFeedback and contributions are welcome.\n\n## Value encoding\n\n1. A clear text message is first assembled as follow:\n\n    [tag][nonce][stamp][value][padding]\n\n  - The tag is 1 byte. The 6 most significant bits encode the version number\n    of the encoding (currently 0). The 2 less significant bits encode the number\n    of padding bytes (0, 1 or 2). 3 is an invalid padding length. The number is\n    picked so that the total length including the MAC is a multiple of 3. This\n    simplifies base64 encoding by avoiding its padding.\n  - The nonce is 16 bytes long (AES block length) and contains cryptographically\n    secure pseudorandom bytes.\n  - The stamp is the unix time subtracted by an epochOffset value (1505230500),\n    and encoded using the [LEB128 encoding](https://en.wikipedia.org/wiki/LEB128).\n  - The value is a copy of the user provided value to secure.\n  - The padding bytes are cryptographically secure pseudorandom bytes. There may\n    be 0 to 2 padding bytes. The number is picked so that the total length\n    including the MAC is a multiple of 3. This simplifies base64 encoding by\n    avoiding its padding.\n\n2. The stamp, value and padding bytes are ciphered using CTR-AES with the 16 last\n   bytes of the key as ciphering key. The nonce is used as iv and counter\n   initialization value. The tag and nonce are left unciphered.\n\n3. An HMAC-SHA-256 is computed over (1) the cookie name and (2) the bytes sequence\n   obtained after step 2. The 32 byte long MAC is appended after the padding.\n\n4. The whole byte sequence, from the tag to the last byte of the MAC is encoded in\n   [Base64 using the URL encoding](https://tools.ietf.org/html/rfc4648#section-5).\n   There is no padding since the byte length is a multiple of 3 bytes.\n\nThe tag which provides an encoding version allows completely changing the encoding\nwhile preserving backwards compatibility if required.\n\n## Contributors\n\n- lstokeworth (reddit):\n  - suggest to replace `copy` with `append`,\n  - remove the Expires Params field and use only the MaxAge,\n  - provide a constant date in the past for Delete.\n- cstockton (github):\n  - critical [bug report](https://github.com/chmike/cookie/issues/1),\n  - suggest simpler API.\n- flowonyx (github):\n  - fix many typos in the README and comments.\n\n## Usage advise\n\nIt is very important to understand that the security is limited to the cookie\ncontent. Nothing proves that the other data received by the server with a\nsecure cookie has been created by the user's request.\n\nWhen you have properly set the domain path, and HTTPOnly with the\nSecure flag, and use HTTPS, only the user's browser can send the cookie.\nBut it is still possible for an attacker to trick your browser to send\na request to the site without the user's knowledge and consent. This is known as\na [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) attack.\n\nConsider this scenario with a Pizza ordering web site. First let's see what\nhappens when everything goes as expected.\n\nThe user has to login to the site to be allowed to order pizzas. During the\nlogin transaction a secure cookie is added into the user's browser. The user\nis then shown a form with the number of pizzas to order. When the user clicks\nthe *Order* button, his browser will make a request to an URL provided with\nthe form. It will join the field values and the secure cookie since the URL\npath and domain match the one specified at the login transaction.\n\nWhen the server receives this request, it checks the cookie validity to\ndetermine who that client is and if he is legitimate. All is fine. The order\nis then forwarded to the pizza chef. The pizza is later delivered to the\nuser.\n\nNow comes the villain. He sets up some random site with a form and a validation\nbutton that the victim will very likely click (e.g., \"Subscribe to spam\" with\na *Please no* button as validation button). The villain has set up the form\nso that the URL associated to the validation button is the URL to order pizzas.\nHe will have added a hidden field with the number of pizzas to order set to 10!\n\nWhen the user clicks that validation button, his browser will send a request\nto the pizza ordering site with the field value and the secure cookie since the\nURL matches the cookie path and domain.\n\nThe pizza ordering site checks the secure cookie and it will be authenticated.\nIt will assume that the user issued that order. When the delivery man rings at\nthe user's door with 10 pizzas in his hand, there will be a conflict and no way\nto know to know who's fault it is. Notice how the value 10 associated with the\ncookie in the ordering request was not signed by the user's browser.\n\nTo avoid this, the solution is to add a way to authenticate the form response.\nThis is done by adding a hidden field in the form with a random byte sequence,\nand set a secure cookie with that byte sequence as value and a validity date\nlimit. When the user fills that form, the server will receive back the secure\ncookie and the hidden field value. The server then checks that they match to\nvalidate the response.\n\nAn attacker can forge a random byte sequence, but can't forge the secure cookie\nthat goes with it.\n\nNote that this protection is void in case of XSS attack (script injection).\n\nThe above method works with forms, not with REST API like requests because the\nserver can't send the random token to the client that can be used as a challenge.\nFor REST API like authenticated transactions, the client and server have to\nboth know a secret byte sequence they use to compute a hmac value over the URI,\nthe method, the data and a message sequence number. They can then authenticate\nthe message and the source.\n\nThe secret byte sequence can be determined in the authentication transaction\nwith public and private keys. There is no need for TLS to securely authenticate\nthe client and server. A secret cookie is no help here.\n","funding_links":["https://www.buymeacoffee.com/Chmike"],"categories":["Authentication and OAuth","身份验证和OAuth","Authentication and Authorization","认证和OAuth授权","Uncategorized","\u003cspan id=\"身份验证和oauth-authentication-and-auth\"\u003e身份验证和OAuth Authentication and Auth\u003c/span\u003e"],"sub_categories":["Contents"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchmike%2Fsecurecookie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchmike%2Fsecurecookie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchmike%2Fsecurecookie/lists"}