diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..2eea525d885d5148108f6f3a9a8613863f783d36 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..c3dcee3439edd0b08da020c1b33f08b13f227773 --- /dev/null +++ b/.env @@ -0,0 +1,8 @@ +OKTA_DOMAIN=https://dev-80790093.okta.com +OKTA_CLIENT_ID=0oa2v2ujqfpqiAuEk5d7 +OKTA_CLIENT_SECRET=7zrwJbmIT60dqQAJiqS0pu0VQT7FAVo4lzDqG2Hi +OKTA_AUTHORIZE_URL=/oauth2/default/v1/authorize +OKTA_REDIRECT_URI=http://localhost/authorization-code/callback +OKTA_TOKEN_URL=/oauth2/default/v1/token +BASIC_AUTH_LOGINS="foo dylan" +BASIC_AUTH_PASSWORDS="bar Super" \ No newline at end of file diff --git a/.gitignore b/.gitignore index e15ad59db1ce287043acdb3bd04d09951babfa80..ccdfaafd7b3ac8083b884356c4ab5524e1b2e330 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .idea logs/ -.env diff --git a/Dockerfile b/Dockerfile index 15f9d6eeb18ea4556d7ea4f69c152e4d96c22b01..b3101e9815c63aa31995e5b36d0dca2119dbe602 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ RUN go mod download COPY . . + # Build the application into appSec executable RUN go build -o /appSec diff --git a/README.md b/README.md index f86955ed718171919d2b57acea794a45dda4880a..ccfc5347d30ded5809c5f2fbd184a8f2ba929465 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,105 @@ +# Overview +The application offers through two distincts endpoints, information about students and teachers. +The application requires two types of authentication depending on the endpoint accessed. + +# Details +## Infrastructure +Different components are used throughout the application. +* A reverse proxy, using nginx to allow HTTPS connections only +* An Identity Provider, OKTA, to authenticate users on **teachers** endpoints with the help of an **access token** +* Basic auth to authenticate users on **students** endpoints. +## Code architecture +To organize the code better, I've split the code into 4 files. +* **main.go**, entry point of the application, runs the program and calls needed functions +* **pkg/api/auth/okta.go**, all logic useful to authenticate using Okta Identity Provider +* **pkg/api/messages/messages.go**, some constant messages used through the code +* **pkg/api/middlewares/authentication.go**, middlewares used before calling endpoints methods to verify user authentication + +### Environment variables +It's a good practice to store sensitive data or simply data that varies from developer to developer in an environment file. +The file is usually name **.env**, where we can store key -> pair values to load dynamically into the code at compile time. + +For this application, I've stored 8 variables into the **.env** file. + +| Variable | Description | +|---|---| +|OKTA_DOMAIN| Okta domain url| +|OKTA_CLIENT_ID| Client id, used to validate access token and call /token-request| +|OKTA_CLIENT_SECRET| Client secret, used to authenticate when calling /token-request| +|OKTA_AUTHORIZE_URL| Okta URL used to authenticate on a browser, returning a code | +|OKTA_REDIRECT_URI| Okta redirect uri| +|OKTA_TOKEN_URL| URL to retrieve an **access token** when passing the **code** from the authorize url | +|BASIC_AUTH_LOGINS| List of basic auth usernames separated by spaces | +|BASIC_AUTH_PASSWORDS| List of passwords matching usernames order separated by spaces| + +### main.go +This file is the entry point of the application. It will set up users permissions, load basic auth credentials from **.env** file and define the endpoints. + +All the endpoints functions called when accessed, are defined in this file aswell. +### okta.go +This file contains logic required for the Okta Identity Provider to work. + +### authentication.go +This file contains methods related to authentication. +One method is used to init users permissions (**InitUserPermissions**), and the other three are middlewares. + +The middlewares functions are called functions that will be called right before calling the linked endpoint function. +Their job is to check whether the request is authorized to execute the next request. + +The function **Authorization** takes a **username** and will check it against the map of users permissions. +If there is a match, the next request on the line will be called using **c.Next()**, if there is not match, we abort the request with a **Forbidden** error. + +The functions **BasicAuthorization** and **JWTAuthorization** both call **Authorization**, they just get the username from a different source. +**BasicAuthorization** get it from the basic auth credentials and **JWTAuthorization** from the **sub** field in the **claims** of the **access token**. + +## Endpoints +An endpoint is an URL where any authorized user can make request to retrieve or send information. +This application implements 3 differents endpoints +* One for the students located at **/students**, requires basic authentication +* One for the teachers located at **/teachers**, requires OKTA authentication +* One to request an access token located at **/token-request** + +| Method | URL | Description | Params | +|---|---|---|---| +| GET | /students | Get the list of students | None | +| GET | /students/:id | Get the details of a student | None | +| POST | /students | Add a new student | id, lastName, name, orientation | +| DELETE | /students/:id | Delete a student | None | +| GET | /teachers | Get the list of teachers | None | +| GET | /teachers/:id | Get the details of a teacher | None | +| POST | /teachers | Add a new teacher | id, lastName, name, class | +| DELETE | /teacher/:id | Delete a teacher | None | +| GET | /token-request | Execute the process to retrieve an access token easily | None | + +### Postman +The file **endpoints_postman.json** contains all useful endpoints to test for the application. +You can test the different endpoints using different credentials. + +See below a list of which users have access to which endpoints + +| Method | Endpoint | Users | +|---|---|---| +| GET | /students | foo, dylan | +| GET | /students/:id | foo, dylan | +| POST | /students | dylan | +| DELETE | /students/:id | dylan | +| GET | /teachers | viewer@appsec.ch, adder@appsec.ch, destroyer@appsec.ch | +| GET | /teachers/:id | viewer@appsec.ch, adder@appsec.ch, destroyer@appsec.ch | +| POST | /teachers | adder@appsec.ch | +| DELETE | /teachers/:id | destroyer@appsec.ch | + + +## Authentication and authorization +### Okta token request +For the Okta authentication, we need to retrieve an **access token** from their Identity Provider so that we can pass it to the application and verify it server-side. +The steps to get an access token are the following : +* Authenticate using Okta Authorize URL on Postman to retrieve a **code** +* Pass this **code** to the GET Token URL to retrieve the access token giving access to the teachers endpoint. + +To make this easier to test, I implemented the logic above into code so that we can get an **access token** simply by calling the **/token-request** endpoint. + + +## Setup # How to build the app ```sh docker build --tag appsec . @@ -5,10 +107,21 @@ docker build --tag appsec . # How to run the app ```sh -docker run --publish 3000:8080 appsec -``` -OR -```sh -sudo docker-compose up -d sudo docker-compose up -D //attach to shell ``` + +# How to test the application +First, run the application using the command above. +You can test the **/students** endpoints which requires only basic auth with one of the follow login +* foo:bar (only GET requests) +* dylan:Super (GET, POST, DELETE requests) + +For the **/teachers** endpoints, it requires authentication via Okta, to do so, call the **/token-request** URL. +It will handle all of the stuff to get your **access token**. +To authenticate on Okta, use one of the following accounts +* viewer@appsec.ch:SuperHEPIA22 (GET requests only) +* adder@appsec.ch:SuperHEPIA22 (GET, POST requests only) +* destroyer@appsec.ch:SuperHEPIA22 (GET, DELETE requests only) + +Once you have your **access token**, you can test the endpoints directly on postman with the file named **endpoints_postman.json** +By passing the **access_token** in the Authorization tab -> Type: Bearer Token. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f3010ce8e97ccd3dd02a4f1bd6320a52dc31dca4..b2893b3c345154ec3c9db650fdbfafbde81e45e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,5 +14,7 @@ services: appsec: image: appsec:latest container_name: appsec + env_file: + - .env expose: - "8080" \ No newline at end of file diff --git a/docs/analyse.txt b/docs/analyse.txt deleted file mode 100644 index 403b69553d33e95b3e6bb4017c975dd761bc4ada..0000000000000000000000000000000000000000 --- a/docs/analyse.txt +++ /dev/null @@ -1,442 +0,0 @@ -Report for 10.136.216.249 - ------------------------------------------------------------- - -Versions - -SSL 2.0 : false -SSL 3.0 : false -TLS 1.0 : true -TLS 1.1 : true -TLS 1.2 : true -TLS 1.3 : false - ------------------------------------------------------------- - -Supported Cipher suites - -TLS_RSA_WITH_AES_256_CCM_8 -TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -TLS_RSA_WITH_AES_128_CCM_8 -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 -TLS_RSA_WITH_AES_128_CCM -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 -TLS_RSA_WITH_AES_256_CBC_SHA256 -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_AES_256_CCM -TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -TLS_RSA_WITH_AES_128_GCM_SHA256 -TLS_RSA_WITH_ARIA_256_GCM_SHA384 -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA -TLS_RSA_WITH_AES_128_CBC_SHA -TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -TLS_RSA_WITH_ARIA_128_GCM_SHA256 -TLS_RSA_WITH_AES_128_CBC_SHA256 -TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 -TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 -TLS_RSA_WITH_AES_256_GCM_SHA384 -TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 -TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 -TLS_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 - ------------------------------------------------------------- - -Supported in TLS 1.0 - -TLS_RSA_WITH_AES_128_CBC_SHA -TLS_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ------------------------------------------------------------- - -Supported in TLS 1.1 - -TLS_RSA_WITH_AES_128_CBC_SHA -TLS_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ------------------------------------------------------------- - -Supported in TLS 1.2 - -TLS_RSA_WITH_AES_128_CBC_SHA -TLS_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_AES_128_CBC_SHA256 -TLS_RSA_WITH_AES_256_CBC_SHA256 -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA -TLS_RSA_WITH_AES_128_GCM_SHA256 -TLS_RSA_WITH_AES_256_GCM_SHA384 -TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 -TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 -TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -TLS_RSA_WITH_ARIA_128_GCM_SHA256 -TLS_RSA_WITH_ARIA_256_GCM_SHA384 -TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 -TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 -TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 -TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 -TLS_RSA_WITH_AES_128_CCM -TLS_RSA_WITH_AES_256_CCM -TLS_RSA_WITH_AES_128_CCM_8 -TLS_RSA_WITH_AES_256_CCM_8 -TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - ------------------------------------------------------------- - -Perfect Forward Secrecy - -Supports PFS : true -Prefers PFS : false -Supports Only PFS : false - ------------------------------------------------------------- - -CipherSuite General - -Enforces CipherSuite ordering : false - ------------------------------------------------------------- - -Supported Extensions - -MAX_FRAGMENT_LENGTH -ALPN -ENCRYPT_THEN_MAC -EXTENDED_MASTER_SECRET -SESSION_TICKET -RENEGOTIATION_INFO - ------------------------------------------------------------- - -Extensions - -Secure Renegotiation : true -Extended Master Secret : true -Encrypt Then Mac : true -Tokenbinding : false -Certificate Status Request : false -Certificate Status Request v2 : false -ESNI : not tested yet - ------------------------------------------------------------- - -TLS 1.3 Named Groups - -none - ------------------------------------------------------------- - -Supported Named Groups - -SECP256R1 -ECDH_X25519 -SECP384R1 -SECP521R1 -ECDH_X448 - ------------------------------------------------------------- - -Supported Compressions - -NULL - ------------------------------------------------------------- - -Elliptic Curve Point Formats - -Uncompressed : true -ANSIX962 Prime : true -ANSIX962 Char2 : false -TLS 1.3 ANSIX962 SECP : could not test - ------------------------------------------------------------- - -Record Fragmentation - -Supports Record Fragmentation : true - ------------------------------------------------------------- - -ALPN - -http/1.1 : true - ------------------------------------------------------------- - -Common Bugs [EXPERIMENTAL] - -Version Intolerant : false -CipherSuite Intolerant : false -Extension Intolerant : false -CS Length Intolerant (>512 Byte) : false -Compression Intolerant : false -ALPN Intolerant : false -CH Length Intolerant : false -NamedGroup Intolerant : false -Empty last Extension Intolerant : false -SigHashAlgo Intolerant : false -Big ClientHello Intolerant : false -2nd CipherSuite Byte Bug : false -Ignores offered Cipher suites : false -Reflects offered Cipher suites : false -Ignores offered NamedGroups : false -Ignores offered SigHashAlgos : false -Grease CipherSuite Intolerant : false -Grease NamedGroup Intolerant : false -Grease SigHashAlgo Intolerant : false - ------------------------------------------------------------- - -TLS 1.3 Hello Retry Request - -Sends Hello Retry Request : error -Issues Cookie : error - ------------------------------------------------------------- - -Attack Vulnerabilities - -Padding Oracle : not vulnerable -Bleichenbacher : not vulnerable -Raccoon : not vulnerable -Direct Raccoon : could not test (not vulnerable) -CRIME : not vulnerable -Breach : not vulnerable -Invalid Curve : not vulnerable -Invalid Curve (ephemeral) : not vulnerable -Invalid Curve (twist) : not vulnerable -SSL Poodle : not vulnerable -TLS Poodle : not vulnerable -Logjam : not vulnerable -Sweet 32 : not vulnerable -General DROWN : not vulnerable -Extra Clear DROWN : not vulnerable -Heartbleed : not vulnerable -EarlyCcs : not vulnerable -CVE-2020-13777 (Zero key) : not vulnerable -ALPACA : not mitigated -Renegotiation Attack (ext) : not vulnerable -Renegotiation Attack (cs) : not vulnerable - ------------------------------------------------------------- - -Alpaca Details - -Strict ALPN : false -Strict SNI : false -ALPACA Mitigation : not mitigated - ------------------------------------------------------------- - -Bleichenbacher Details - -CKE_CCS_FIN | No Behavior Difference -CKE | No Behavior Difference -CKE_CCS | No Behavior Difference -CKE_FIN | No Behavior Difference - ------------------------------------------------------------- - -PaddingOracle response map - -No vulnerability present to identify - ------------------------------------------------------------- - -Padding Oracle Details - -CLASSIC_DYNAMIC SHORT TLS10 TLS_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS10 TLS_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS10 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS10 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS10 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS10 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS11 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_AES_128_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_AES_256_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 | No behavior difference | NOT VULNERABLE | P: 1.000 -CLASSIC_DYNAMIC SHORT TLS12 TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 | No behavior difference | NOT VULNERABLE | P: 1.000 - ------------------------------------------------------------- - -Direct Raccoon Results - -No test results - ------------------------------------------------------------- - -Invalid Curve Details - -No Vulnerabilities found - ------------------------------------------------------------- - -Certificate Chain (Certificate 1 of 1) - -Chain ordered : true -Contains Trust Anchor : false -Generally Trusted : false ---|Certificate Issues - -Your server did not provide a certificate which is valid for the scanned domain ---|Certificate #1 - - Subject : C=CH,ST=Geneva,L=Geneva,O=HEPIA - Issuer : C=CH,ST=Geneva,L=Geneva,O=HEPIA - Valid From : Tue Oct 19 15:19:50 GMT 2021 - Valid Till : Wed Oct 19 15:19:50 GMT 2022 - Expires in : 343 days. - PublicKey Type: : RSA - Modulus : d3638ee09eee45d6488d79cdfa370fac0ae33bf2492402a5cfb2fdda752e461c7f8cb8f31f4def6651da813b7765cad5ea58738c610eb40f2606f6a22ed1b153b06ff8d843fbfd236207c7700f32e96dcd37acc93e7c5259e8cd8cee6bc02612da3fed2940d4f4735404ba29842379575fefa5eff32f370524674cfe8956ad69ae327d0ba8ce70e0fc83857dc4304797f5fc710bbdecf807a5c1b6970955c97e9c061bea4c84ece21e8a48c03969920f4a8b57e25a61420b8dfe111ccfd4f449907647a52747f48b42e04d9e7297786c31d24055e168e3fe9586195f1c0befbbe5ab42c9b806cc8e5553a82e95f83a971c47a627191b2acb69c1eb148b5748cb3c2c0813eb7c4996df77b352a6c1fba81ecb93b63e911abb586722cd30adf80da17056c77212f41f9dc4676123a6152a837da79b603cfa6fc83790cdbcfc81a206291f70fac94f00af913100713802d15f93c613c8de3bae6f2a5de732d3de3158074f5dd15fce0ef648711457d999bdb71e42d5880695ea61a4a41ba3a24afd5dc85ea6a1a6a657d195c9cefe666a977171f4cce2b1a80e8c2b76233dec885d37417e39ae9378983b20dd23bd5257aeae4d4e97aeccdf34ccc947b7f3df9eb442589a30acacc8a8aab5c994416be1e6d89bcd4caf03de719e6498e51e2c26a6000b701a438ef9ecfd184c5a916d40b5662342bf9af0eb81ad0d09db8b2c271d - Public exponent : 10001 - Signature Algorithm : RSA - Hash Algorithm : SHA256 - OCSP Supported : false - OCSP must Staple : false - ROCA (simple) : false - Fingerprint (SHA256) : aa79163d1bd410b42ad350fda0dd4addb11a7cd708d1539e769ba0b11f8f9f36 - ------------------------------------------------------------- - -OCSP - -Supports OCSP : false -OCSP Stapling : false -Must Staple : false -OCSP Stapling (TLS 1.3) : false -Multi Stapling (TLS 1.3) : false -Supports Nonce : false ---|Detailed OCSP results for certificate 1 of 1 - - Supports Nonce : false - Nonce Mismatch / Cached Nonce : false - ------------------------------------------------------------- - -Certificate Transparency - -Supports Precertificate SCTs : false -Supports TLS Handshake SCTs : false -Supports OCSP Response SCTs : false -Meets Chrome's CT Policy : false - ------------------------------------------------------------- - -Session - -Supports Session Resumption : false -Supports Session Tickets : true -Issues TLS 1.3 Session Tickets : false -Supports TLS 1.3 PSK : false -Supports TLS 1.3 PSK-DHE : false -Supports 0-RTT : false - ------------------------------------------------------------- - -Renegotioation - -Secure (Extension) : false -Secure (CipherSuite) : false -Insecure : false - ------------------------------------------------------------- - -HSTS - -Not supported - ------------------------------------------------------------- - -HPKP - -Not supported - ------------------------------------------------------------- - -HTTPS Response Header - -Server:nginx/1.21.3 -Date:Tue, 09 Nov 2021 16:40:38 GMT -Content-Type:text/plain -Content-Length:18 -Connection:keep-alive - ------------------------------------------------------------- - -HTTP False Start - -HTTP False Start : true - ------------------------------------------------------------- - -Nonce - -Random : no duplicates (wip) - ------------------------------------------------------------- - -PublicKey Parameter - -EC PublicKey reuse : false -DH PublicKey reuse : could not test (no) -Uses Common DH Primes : could not test (no) -Uses only prime moduli : could not test (no) -Uses only safe-prime moduli : could not test (no) - ------------------------------------------------------------- - -Client authentication - -Supported : false -Required : false - ------------------------------------------------------------- - -Scoring results - -Score: 1050 - ------------------------------------------------------------- - -Recommendations - -The TLS does not reject invalid SNI names.. If possible configure your server to use strict SNI and strict ALPN verification -TLS 1.0 is enabled. Consider disabling TLS 1.0 -RSA key exchange is enabled. Disable RSA key exchange -HSTS is disabled. Enable HSTS -The TLS server does not reject unsupported ALPN Strings. If possible configure your server to use strict ALPN verification -TLS 1.3 is disabled. Enable TLS 1.3 -PFS cipher suites are not preferred. Enable cipher suite ordering and prefer PFS cipher suites -Cipher suite ordering is disabled. Enable cipher suite ordering -The TLS server does not reject invalid SNI names.. If possible configure your server to use strict SNI verification diff --git a/docs/rapport.md b/docs/rapport.md deleted file mode 100644 index 8f41e9a8186696222cdbebba9dfd1c56fce04bbc..0000000000000000000000000000000000000000 --- a/docs/rapport.md +++ /dev/null @@ -1,41 +0,0 @@ -# TLS Protection - -## Options to harden TLS configuration - -### ssl_ciphers - -Define ciphers suite used for SSL. -By default, **MD5** is used but it's not considered secure anymore. - -Order matters -One of the suite of ciphers we could use is the follow: - -- **Key Exchange Algorithm** : ECDH -- **Authentication Algorithm** : RSA -- **Bulk Encryption Algorithm** : AES256-GCM -- **Mac Algorithm** : SHA384 - -[TLS hardening](https://www.acunetix.com/blog/articles/tls-ssl-cipher-hardening/) - -Cette option permet de définir les algorithmes de chiffrement. -Par défaut, **MD5** est utilisé et cet algorithme n'est plus considéré comme sûr. -Il serait donc envisageable, par exemple, d'utiliser du **RSA**. - -### ssl_protocols - -Activer tls1.2 et tls1.3 seulement - -### ssl_password_file - -### ssl_prefer_server_ciphers - -The server owner can control which ciphers suite are available. -Off by default, not secure. - - - -# Authentication & Authorization - -For the /teachers endpoints, implement authentication and authorization using Okta as IdP with access_tokens -First require code to : https://dev-80790093.okta.com/oauth2/default/v1/authorize -Then trade code for an access token that will give access to the API https://dev-80790093.okta.com/oauth2/default/v1/token diff --git a/endpoints_postman.json b/endpoints_postman.json new file mode 100644 index 0000000000000000000000000000000000000000..651e876bfb9e1decdbd320f171ff15439b805a72 --- /dev/null +++ b/endpoints_postman.json @@ -0,0 +1,339 @@ +{ + "info": { + "_postman_id": "e88d6235-0e63-4028-9949-c7257bc76c29", + "name": "AppSec API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "OKTA", + "item": [ + { + "name": "GET Authorize (code)", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://dev-80790093.okta.com/oauth2/default/v1/authorize?client_id=&response_type=code&scope=openid&redirect_uri=&state=100", + "protocol": "https", + "host": [ + "dev-80790093", + "okta", + "com" + ], + "path": [ + "oauth2", + "default", + "v1", + "authorize" + ], + "query": [ + { + "key": "client_id", + "value": "" + }, + { + "key": "response_type", + "value": "code" + }, + { + "key": "scope", + "value": "openid" + }, + { + "key": "redirect_uri", + "value": "" + }, + { + "key": "state", + "value": "100" + } + ] + } + }, + "response": [] + }, + { + "name": "GET Token (access token)", + "request": { + "auth": { + "type": "bearer" + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "url": { + "raw": "https://dev-80790093.okta.com/oauth2/default/v1/token?grant_type=authorization_code&redirect_uri=&code=", + "protocol": "https", + "host": [ + "dev-80790093", + "okta", + "com" + ], + "path": [ + "oauth2", + "default", + "v1", + "token" + ], + "query": [ + { + "key": "grant_type", + "value": "authorization_code" + }, + { + "key": "redirect_uri", + "value": "" + }, + { + "key": "code", + "value": "" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "GET students", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + }, + { + "name": "GET students/:id", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "bar", + "type": "string" + }, + { + "key": "username", + "value": "foo", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://localhost/students/1", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "students", + "1" + ] + } + }, + "response": [] + }, + { + "name": "POST students", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "Super", + "type": "string" + }, + { + "key": "username", + "value": "dylan", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": 4,\r\n \"lastName\": \"Doe\",\r\n \"name\":\"Jane\",\r\n \"orientation\": \"agro\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://localhost/students", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "students" + ] + } + }, + "response": [] + }, + { + "name": "DELETE students/:id", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "Super", + "type": "string" + }, + { + "key": "username", + "value": "dylan", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "https://localhost/students/4", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "students", + "4" + ] + } + }, + "response": [] + }, + { + "name": "GET teachers", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + }, + { + "name": "GET teachers/:id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJraWQiOiJ0NUlkRWJubHZEZ3A1Yl90dDhCdVFEMnE2dFhpb1BoZW5nanBKNG04S2NRIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULms4TUprQl8yNklfY2JIVjg0VjcxbVJNdFJYZi1FajdYV25wX1dyWnBhSjgiLCJpc3MiOiJodHRwczovL2Rldi04MDc5MDA5My5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE2NDI1OTE3NzIsImV4cCI6MTY0MjU5NTM3MiwiY2lkIjoiMG9hMnYydWpxZnBxaUF1RWs1ZDciLCJ1aWQiOiIwMHUydjJzcmwxY090ZWQ1ZDVkNyIsInNjcCI6WyJvcGVuaWQiXSwic3ViIjoidmlld2VyQGFwcHNlYy5jaCIsInVzZXIiOiJ2aWV3ZXIifQ.dMG1sex4muWOr74Gq8fOylbJvuth_SJiNbjfmu4a8gHu0lqaIXo-lBWQ5qcEiAoevJnDIqkFSNU4af0G8tvuxzLLNwCrvMktUtxGAv_kyGb8KGpDYG-UyZrqzIKZ4V751l-ylz9Si2j4DkDskCrAa07qbRme3bPfMo7M_ZCUA7G8tG0GxaDwi6VDPsRfLfzcSy84n-_jzK1Mb1AZ5RUfA0eDugePoo1WqZ0xL2AwOjcfNiuJMC5jYAdZZTLeM2vDvhE3AjPsKRRIP6234z4APs3_Adfz_U0_2kNWRfbVQn4dOpCuX5n4EiL7dNXm60YVdnSZa9i_ew90p2lHlJ7fMg", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://localhost/teachers/1", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "teachers", + "1" + ] + } + }, + "response": [] + }, + { + "name": "POST teachers", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "Super", + "type": "string" + }, + { + "key": "username", + "value": "dylan", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": 4,\r\n \"lastName\": \"Doe\",\r\n \"name\":\"Jane\",\r\n \"orientation\": \"agro\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://localhost/students", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "students" + ] + } + }, + "response": [] + }, + { + "name": "DELETE teachers/:id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJraWQiOiJ0NUlkRWJubHZEZ3A1Yl90dDhCdVFEMnE2dFhpb1BoZW5nanBKNG04S2NRIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULms4TUprQl8yNklfY2JIVjg0VjcxbVJNdFJYZi1FajdYV25wX1dyWnBhSjgiLCJpc3MiOiJodHRwczovL2Rldi04MDc5MDA5My5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE2NDI1OTE3NzIsImV4cCI6MTY0MjU5NTM3MiwiY2lkIjoiMG9hMnYydWpxZnBxaUF1RWs1ZDciLCJ1aWQiOiIwMHUydjJzcmwxY090ZWQ1ZDVkNyIsInNjcCI6WyJvcGVuaWQiXSwic3ViIjoidmlld2VyQGFwcHNlYy5jaCIsInVzZXIiOiJ2aWV3ZXIifQ.dMG1sex4muWOr74Gq8fOylbJvuth_SJiNbjfmu4a8gHu0lqaIXo-lBWQ5qcEiAoevJnDIqkFSNU4af0G8tvuxzLLNwCrvMktUtxGAv_kyGb8KGpDYG-UyZrqzIKZ4V751l-ylz9Si2j4DkDskCrAa07qbRme3bPfMo7M_ZCUA7G8tG0GxaDwi6VDPsRfLfzcSy84n-_jzK1Mb1AZ5RUfA0eDugePoo1WqZ0xL2AwOjcfNiuJMC5jYAdZZTLeM2vDvhE3AjPsKRRIP6234z4APs3_Adfz_U0_2kNWRfbVQn4dOpCuX5n4EiL7dNXm60YVdnSZa9i_ew90p2lHlJ7fMg", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "https://localhost/teachers/3", + "protocol": "https", + "host": [ + "localhost" + ], + "path": [ + "teachers", + "3" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/main.go b/main.go index bf30d5a354694322f5ef86d91079c06d7d608b1d..880e723b0724549bb48e619bfb925c1493a588d7 100644 --- a/main.go +++ b/main.go @@ -2,16 +2,14 @@ package main import ( "appSec/pkg/api/auth" + "appSec/pkg/api/messages" "appSec/pkg/api/middlewares" - "fmt" - "log" "net/http" "os" "strconv" "strings" "github.com/gin-gonic/gin" - "github.com/joho/godotenv" ) type student struct { @@ -30,33 +28,27 @@ type teacher struct { var students = []student{ {ID: 1, LastName: "Peiry", Name: "Dylan", Orientation: "Logiciel"}, + {ID: 2, LastName: "Troller", Name: "Fabian", Orientation: "Sécurité"}, + {ID: 3, LastName: "Sirey", Name: "Loic", Orientation: "Matériel"}, +} +var teachers = []teacher{ + {ID: 1, LastName: "Maréchal", Name: "Christophe", Class: "c lang"}, + {ID: 2, LastName: "Terrier", Name: "Anne", Class: "databases"}, } -var teachers []teacher func main() { - err := godotenv.Load() - if err != nil { - log.Fatal("Error loading ..env file") - } - middlewares.InitUserPermissions() router := gin.Default() - var accounts map[string]string - accounts = make(map[string]string) + accounts := make(map[string]string) //Load basic auth crendentials from dotenv file - var loginsEnv string - var passwordsEnv string - loginsEnv = os.Getenv("BASIC_AUTH_LOGINS") - passwordsEnv = os.Getenv("BASIC_AUTH_PASSWORDS") - logins := strings.Fields(loginsEnv) - passwords := strings.Fields(passwordsEnv) + logins := strings.Fields(os.Getenv("BASIC_AUTH_LOGINS")) + passwords := strings.Fields(os.Getenv("BASIC_AUTH_PASSWORDS")) //Store loaded credentials for k, v := range logins { password := passwords[k] - fmt.Println(v, password) accounts[v] = password } @@ -82,7 +74,7 @@ func main() { router.GET("/token-request", auth.RequestCode) router.GET("/authorization-code/callback", auth.ExchangeCodeForJWT) - err = router.Run(":8080") + err := router.Run(":8080") if err != nil { return } @@ -127,7 +119,7 @@ func getStudentById(c *gin.Context) { return } } - c.IndentedJSON(http.StatusNotFound, gin.H{"message": "student not found"}) + c.IndentedJSON(http.StatusNotFound, gin.H{"message": messages.StudentNotFound}) } func getTeacherById(c *gin.Context) { @@ -139,7 +131,7 @@ func getTeacherById(c *gin.Context) { return } } - c.IndentedJSON(http.StatusNotFound, gin.H{"message": "teacher not found"}) + c.IndentedJSON(http.StatusNotFound, gin.H{"message": messages.TeacherNotFound}) } func deleteStudentByID(c *gin.Context) { @@ -156,7 +148,7 @@ func deleteStudentByID(c *gin.Context) { } } - c.IndentedJSON(http.StatusNotFound, gin.H{"message": "student not found"}) + c.IndentedJSON(http.StatusNotFound, gin.H{"message": messages.StudentNotFound}) } func deleteTeacherById(c *gin.Context) { @@ -174,5 +166,5 @@ func deleteTeacherById(c *gin.Context) { } } - c.IndentedJSON(http.StatusNotFound, gin.H{"message": "teacher not found"}) + c.IndentedJSON(http.StatusNotFound, gin.H{"message": messages.TeacherNotFound}) } diff --git a/pkg/api/messages/messages.go b/pkg/api/messages/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..af7a6bcd710c1bb46ac69d46ad543ab04ad2eb10 --- /dev/null +++ b/pkg/api/messages/messages.go @@ -0,0 +1,4 @@ +package messages + +const StudentNotFound string = "student not found" +const TeacherNotFound string = "teacher not found" diff --git a/pkg/api/middlewares/authentication.go b/pkg/api/middlewares/authentication.go index 0e5708080f063f4e04c1e2ee85784eb93a43d52d..ce02fd210a4fab386f7dac90c32869e040b9b395 100644 --- a/pkg/api/middlewares/authentication.go +++ b/pkg/api/middlewares/authentication.go @@ -2,20 +2,20 @@ package middlewares import ( "appSec/pkg/api/auth" - "net/http" - "github.com/gin-gonic/gin" + "net/http" ) var UserPermissions map[string][]string func InitUserPermissions() { UserPermissions = make(map[string][]string) + UserPermissions["foo"] = append(UserPermissions["foo"], "GET") - UserPermissions["dylan"] = append(UserPermissions["dylan"], "GET", "PUT", "DELETE", "POST") - UserPermissions["viewer"] = append(UserPermissions["viewer"], "GET") - UserPermissions["adder"] = append(UserPermissions["adder"], "GET", "POST") - UserPermissions["destroyer"] = append(UserPermissions["destroyer"], "GET", "DELETE") + UserPermissions["dylan"] = append(UserPermissions["dylan"], "GET", "DELETE", "POST") + UserPermissions["viewer@appsec.ch"] = append(UserPermissions["viewer@appsec.ch"], "GET") + UserPermissions["adder@appsec.ch"] = append(UserPermissions["adder@appsec.ch"], "GET", "POST") + UserPermissions["destroyer@appsec.ch"] = append(UserPermissions["destroyer@appsec.ch"], "GET", "DELETE") } // Authorization Middleware to check that the user has access to the method @@ -40,7 +40,7 @@ func BasicAuthorization(c *gin.Context) { func JWTAuthorization(c *gin.Context) { if jwt, ok := auth.VerifyJWT(c); ok { - user := jwt.Claims["user"].(string) + user := jwt.Claims["sub"].(string) Authorization(c, user) } c.AbortWithStatus(http.StatusUnauthorized) diff --git a/postman_collection.json b/postman_collection.json deleted file mode 100644 index 7e73bcc12d5db394509663837fc968bfc47959f0..0000000000000000000000000000000000000000 --- a/postman_collection.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "info": { - "_postman_id": "e88d6235-0e63-4028-9949-c7257bc76c29", - "name": "AppSec API", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "GET Authorize (code)", - "request": { - "auth": { - "type": "noauth" - }, - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Basic foo:bar", - "type": "text", - "disabled": true - } - ], - "url": { - "raw": "https://dev-80790093.okta.com/oauth2/default/v1/authorize?client_id=0oa2v2ujqfpqiAuEk5d7&response_type=code&scope=openid&redirect_uri=http://localhost/authorization-code/callback&state=100", - "protocol": "https", - "host": [ - "dev-80790093", - "okta", - "com" - ], - "path": [ - "oauth2", - "default", - "v1", - "authorize" - ], - "query": [ - { - "key": "client_id", - "value": "0oa2v2ujqfpqiAuEk5d7" - }, - { - "key": "response_type", - "value": "code" - }, - { - "key": "scope", - "value": "openid" - }, - { - "key": "redirect_uri", - "value": "http://localhost/authorization-code/callback" - }, - { - "key": "state", - "value": "100" - } - ] - } - }, - "response": [] - }, - { - "name": "GET Token (access token)", - "request": { - "auth": { - "type": "noauth" - }, - "method": "POST", - "header": [ - { - "key": "Accept", - "value": "application/json", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded", - "type": "text" - }, - { - "key": "Authorization", - "value": "Basic MG9hMnYydWpxZnBxaUF1RWs1ZDc6N3pyd0pibUlUNjBkcVFBSmlxUzBwdTBWUVQ3RkFWbzRsekRxRzJIaQ==", - "type": "text" - } - ], - "url": { - "raw": "https://dev-80790093.okta.com/oauth2/default/v1/token?grant_type=authorization_code&redirect_uri=http://localhost/authorization-code/callback&code=GCqhSYKP0M9W5qYcNL0Y2oWrqG-s8WZAToGZvy8_nwc", - "protocol": "https", - "host": [ - "dev-80790093", - "okta", - "com" - ], - "path": [ - "oauth2", - "default", - "v1", - "token" - ], - "query": [ - { - "key": "grant_type", - "value": "authorization_code" - }, - { - "key": "redirect_uri", - "value": "http://localhost/authorization-code/callback" - }, - { - "key": "code", - "value": "GCqhSYKP0M9W5qYcNL0Y2oWrqG-s8WZAToGZvy8_nwc" - }, - { - "key": "client_id", - "value": "0oa2v2ujqfpqiAuEk5d7", - "disabled": true - } - ] - } - }, - "response": [] - } - ] -} \ No newline at end of file