Skip to content
Snippets Groups Projects
Commit d5e2e0ce authored by dylan.peiry's avatar dylan.peiry
Browse files

feat(done): done

parent b1df4b33
No related branches found
No related tags found
No related merge requests found
.env
\ No newline at end of file
.env 0 → 100644
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
.idea .idea
logs/ logs/
.env
...@@ -15,6 +15,7 @@ RUN go mod download ...@@ -15,6 +15,7 @@ RUN go mod download
COPY . . COPY . .
# Build the application into appSec executable # Build the application into appSec executable
RUN go build -o /appSec RUN go build -o /appSec
......
# 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 # How to build the app
```sh ```sh
docker build --tag appsec . docker build --tag appsec .
...@@ -5,10 +107,21 @@ docker build --tag appsec . ...@@ -5,10 +107,21 @@ docker build --tag appsec .
# How to run the app # How to run the app
```sh ```sh
docker run --publish 3000:8080 appsec
```
OR
```sh
sudo docker-compose up -d
sudo docker-compose up -D //attach to shell 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
...@@ -14,5 +14,7 @@ services: ...@@ -14,5 +14,7 @@ services:
appsec: appsec:
image: appsec:latest image: appsec:latest
container_name: appsec container_name: appsec
env_file:
- .env
expose: expose:
- "8080" - "8080"
\ No newline at end of file
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
# 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
{
"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
...@@ -2,16 +2,14 @@ package main ...@@ -2,16 +2,14 @@ package main
import ( import (
"appSec/pkg/api/auth" "appSec/pkg/api/auth"
"appSec/pkg/api/messages"
"appSec/pkg/api/middlewares" "appSec/pkg/api/middlewares"
"fmt"
"log"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/joho/godotenv"
) )
type student struct { type student struct {
...@@ -30,33 +28,27 @@ type teacher struct { ...@@ -30,33 +28,27 @@ type teacher struct {
var students = []student{ var students = []student{
{ID: 1, LastName: "Peiry", Name: "Dylan", Orientation: "Logiciel"}, {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() { func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading ..env file")
}
middlewares.InitUserPermissions() middlewares.InitUserPermissions()
router := gin.Default() 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 //Load basic auth crendentials from dotenv file
var loginsEnv string logins := strings.Fields(os.Getenv("BASIC_AUTH_LOGINS"))
var passwordsEnv string passwords := strings.Fields(os.Getenv("BASIC_AUTH_PASSWORDS"))
loginsEnv = os.Getenv("BASIC_AUTH_LOGINS")
passwordsEnv = os.Getenv("BASIC_AUTH_PASSWORDS")
logins := strings.Fields(loginsEnv)
passwords := strings.Fields(passwordsEnv)
//Store loaded credentials //Store loaded credentials
for k, v := range logins { for k, v := range logins {
password := passwords[k] password := passwords[k]
fmt.Println(v, password)
accounts[v] = password accounts[v] = password
} }
...@@ -82,7 +74,7 @@ func main() { ...@@ -82,7 +74,7 @@ func main() {
router.GET("/token-request", auth.RequestCode) router.GET("/token-request", auth.RequestCode)
router.GET("/authorization-code/callback", auth.ExchangeCodeForJWT) router.GET("/authorization-code/callback", auth.ExchangeCodeForJWT)
err = router.Run(":8080") err := router.Run(":8080")
if err != nil { if err != nil {
return return
} }
...@@ -127,7 +119,7 @@ func getStudentById(c *gin.Context) { ...@@ -127,7 +119,7 @@ func getStudentById(c *gin.Context) {
return 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) { func getTeacherById(c *gin.Context) {
...@@ -139,7 +131,7 @@ func getTeacherById(c *gin.Context) { ...@@ -139,7 +131,7 @@ func getTeacherById(c *gin.Context) {
return 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) { func deleteStudentByID(c *gin.Context) {
...@@ -156,7 +148,7 @@ 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) { func deleteTeacherById(c *gin.Context) {
...@@ -174,5 +166,5 @@ 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})
} }
package messages
const StudentNotFound string = "student not found"
const TeacherNotFound string = "teacher not found"
...@@ -2,20 +2,20 @@ package middlewares ...@@ -2,20 +2,20 @@ package middlewares
import ( import (
"appSec/pkg/api/auth" "appSec/pkg/api/auth"
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
) )
var UserPermissions map[string][]string var UserPermissions map[string][]string
func InitUserPermissions() { func InitUserPermissions() {
UserPermissions = make(map[string][]string) UserPermissions = make(map[string][]string)
UserPermissions["foo"] = append(UserPermissions["foo"], "GET") UserPermissions["foo"] = append(UserPermissions["foo"], "GET")
UserPermissions["dylan"] = append(UserPermissions["dylan"], "GET", "PUT", "DELETE", "POST") UserPermissions["dylan"] = append(UserPermissions["dylan"], "GET", "DELETE", "POST")
UserPermissions["viewer"] = append(UserPermissions["viewer"], "GET") UserPermissions["viewer@appsec.ch"] = append(UserPermissions["viewer@appsec.ch"], "GET")
UserPermissions["adder"] = append(UserPermissions["adder"], "GET", "POST") UserPermissions["adder@appsec.ch"] = append(UserPermissions["adder@appsec.ch"], "GET", "POST")
UserPermissions["destroyer"] = append(UserPermissions["destroyer"], "GET", "DELETE") UserPermissions["destroyer@appsec.ch"] = append(UserPermissions["destroyer@appsec.ch"], "GET", "DELETE")
} }
// Authorization Middleware to check that the user has access to the method // Authorization Middleware to check that the user has access to the method
...@@ -40,7 +40,7 @@ func BasicAuthorization(c *gin.Context) { ...@@ -40,7 +40,7 @@ func BasicAuthorization(c *gin.Context) {
func JWTAuthorization(c *gin.Context) { func JWTAuthorization(c *gin.Context) {
if jwt, ok := auth.VerifyJWT(c); ok { if jwt, ok := auth.VerifyJWT(c); ok {
user := jwt.Claims["user"].(string) user := jwt.Claims["sub"].(string)
Authorization(c, user) Authorization(c, user)
} }
c.AbortWithStatus(http.StatusUnauthorized) c.AbortWithStatus(http.StatusUnauthorized)
......
{
"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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment