Giter Site home page Giter Site logo

excellarateinc / voyage-api-dotnet Goto Github PK

View Code? Open in Web Editor NEW
16.0 23.0 14.0 5.88 MB

Enterprise grade C# .NET Web Services API implementing industry standard best practices

License: Apache License 2.0

C# 87.64% Batchfile 1.27% CSS 0.31% ASP 0.03% Gherkin 0.78% PowerShell 1.15% Shell 0.45% TSQL 3.41% HTML 4.95%
best-practices owasp database oauth auditing webservices api csharp security standards

voyage-api-dotnet's People

Contributors

aeslinger0 avatar ajayboina avatar alfredoball-lssinc avatar jessearenslss avatar maniphanh avatar rajeshpandalss avatar susheel9 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

voyage-api-dotnet's Issues

Document: security section

** New security section **

  • Create security documentation section to match Java Api Link
  • Modify Java Api specific to match .Net

Security: Brute Force Attack: Lock User Account After X Attempts

A brute force attack (BFA) is when an attacker makes thousands of attempts on an HTTP resource in an attempt to guess common values for an input. For example, guessing a username and password on a login page. In most cases there are many combinations of usernames and passwords, so the attacker needs to be able to execute as many combinations as possible per second to find matches (i.e. 3-5k+).

For certain apps, the user account should be locked after a number of failed attempts so that the user's data is protected if the failures are from an attacker. There are app scenarios where locking a user account would disrupt a legitimate user's ability to use their own account, so it's up to the app owner if they wish to enable this feature. Either way, it's important that the API provide the means to lock user accounts after a specific number of failed login attempts.

Requirements:

  • Store a 'enabled' true/false property in an external property config so that this feature can be turned on/off for any given server and any time.
  • Store a 'max login attempts' property in an external property config so that each app can determine the count that is appropriate
  • Create an event listener or http filter that monitors failed FORM login attempts
  • Create an event listener or http filter that monitors failed BASIC AUTH login attempts
  • Lock user feature approach
    • If the feature is enabled (based on external config property), then execute
    • Lookup the User by the username provided
    • If the User exists, then increment the number of failed login attempts (the User should have a "failedLoginAttempts" property stored on the User object
    • Check if the user's new failedLoginAttempts > max failed login attempts (from property config), then lock the user account (user.isLocked = true or user.isEnabled = false)
      • The user should have a locking or disable feature that would prevent further login
      • The user should be able to complete the forgot password process to unlock/enable their account

Create new sql script and seed script for client

Look at java database table schema called "Client" for all required fields

Create tables script

  • Client table
  • Client role table
  • Client scope table

Create seed script

  • Create seed script for client tables using data similar to Clients.cs
  • Create seed script for client role one basic role client1 and admin role for client2
  • Create seed script to insert three scopes: Prfile, Email and Api

Security: Change App claim type to authorities to match Java Api

Change claim base to authorities base

  • Change app.permission to authorities
  • Change delete.role to api.roles.delete
  • Change delete.user to api.users.delete
  • Remove other claim type only use authorities

List of complete user and role name

  • .Net
    `"app.permission": [
    "assign.role",
    "create.role",
    "delete.role",
    "list.roles",
    "revoke.role",
    "view.role",
    "view.claim",
    "list.users",
    "list.user-claims",
    "view.user",
    "update.user",
    "delete.user",
    "create.user",
    "list.role-claims",
    "delete.role-claim",
    "create.claim",
    "login",
    "list.user-claims"
    ],
    "lss.permission": [
    "list.widgets",
    "view.widget",
    "update.widget",
    "create.widget",
    "delete.widget"
    ]

  • Java
    "authorities": [
    "api.permissions.delete",
    "api.roles.delete",
    "api.roles.update",
    "api.permissions.list",
    "api.permissions.update",
    "api.users.create",
    "api.users.get",
    "api.users.list",
    "api.permissions.get",
    "api.roles.get",
    "api.users.update",
    "api.roles.create",
    "api.users.delete",
    "api.permissions.create",
    "api.roles.list"
    ]

Identify the differences between Java API and .Net API

  • Go through Java API documentation link
  • Go through .Net API documentation link
  • Make a list of all API names that are different
  • Make a list of all matching API name
    • List request differences
    • List response differences

Security: Client Credential authentication

Verify with Java authentication server to make sure they are working same way

How it works in Java

Client Credential authentication /oauth/token

  • Our current implementaion require username, password, client_id, secret and grand_type

  • For client credential we want to only pass in the Client ID and Client Secret and get back an access token

  • returns a JWT bearer token that expires within a few hours

  • grant_type=client_credentials

  • Send the Client ID and Client Secret via basic auth

  • POST to: /oauth/token

  • POST data: client_id, client_secret, grant_type=client_credentials

  • The result will be an access code value

Forgot Username / Password Support

Workflow

NOTE: The entire workflow should be completed outside of an authenticated session. Do not allow the user to ever be authenticated during this process because that would give them access to probe the API and other areas of the server! Once the password reset process is complete, then force the user to authenticate with their new credentials.

  1. User clicks "Login" on a client app
    • Passes the "client_id", "client requested grants", and "redirect_url" to the API /oauth/authorize endpoint
    • API server displays a Login page asking for Username & password using the existing OAuth2 process.
  2. User forgets their username / password and clicks "Forgot Password" link underneath the Password input.
    • The client parameters stored in the session need to somehow be preserved so that when the password is reset, the user can be redirected to the login page and resume the process of authenticating using OAuth2
  3. API Server displays a simple forgot password page with a "username" and "mobile phone number" input field
    • Must enter the username associated with the account
    • Must enter one of the registered mobile phone numbers associated with the account
    • Phone number must have a country code + phone number formatted to the country selected. Must be parsable to a E.164 phone number.
    • A "Cancel" button is displayed that will redirect the user back to the login page (preserving their original client parameters sent in on the /oauth/authorize request).
  4. User clicks "Submit" with a username and mobile phone number
    • API server writes an ActionLog to the database with the "Reset Password Request" action log and the "username" in a discrete field (new column)
    • Query ActionLog table for the given username within the past 20 minutes. If >= 5 Password Reset Request records, then return an error message
      • Error message should read something like "Too many password reset attempts. Password reset is locked for 20 minutes for the requested username.".
      • Do not include the username or phone number in the error message.
    • API Server queries the user database for a User with the given username and mobile phone (make sure the mobilePhone.isDeleted=false)
      • If a User is found, then store the User.id within the HTTP Session for reference within the User Verification process.
    • API Server initiates a verification code text message to the found user's mobile phone
      • Initiate the SMS text message send as an asynchronous background job
      • If there is any delay in the SMS delivery process, then an attacker will be able to tell when they have a match if they are guessing. The delay itself indicates that extra work is being done to send the SMS message.
    • Always returns a message "If the username and phone number are correct, then a verification code will be sent to your mobile phone". Even if the username and mobile phone do not match, always display this message.
    • Store the verification code on the mobile phone record with an expiration, just like the normal verification process
      • Do not use the normal User Verification servlet filter because that requires an authenticated user... the user is not authenticated at this point in the process, so reuse whichever parts of the User Verification process make the most sense.
  5. API Server displays a Verification Code page with a single input for the code
    • A reference to the user.id should be stored in the HTTP session if a valid user was identified in the prior step.
    • Display this page even if a user was not found for the given username + mobile phone number on step 4.
    • Included in a page is a "Cancel" button that will take the user back to the Login page where they can try to reenter their password
      • Be sure to retain the original login request parameters so that the OAuth process will continue after a successful login.
  6. User enters the verification code and clicks submit
    • Query ActionLog table for the given username within the past 20 minutes. If >= 5 "Reset Password: Verification Code Attempt" are found, then return an error message
      • Error message should read something like "Too many password reset attempts. Password reset is locked for 20 minutes for the requested username.".
      • Do not include the username or phone number in the error message.
    • The verification code + the user.id in the HTTP session are used to find a matching mobile phone associated with the user.id.
    • If a user.id is not in the HTTP session, then execute the User lookup anyway using a fictitious user.id of "0" (zero).
      • It's important NOT to give away to an attacker that we are executing a different process for nonexistent users.
      • By conducting the same workflow even tho we know the user doesn't exist, we are keeping the latency between calls almost exactly the same. Virtually unnoticeable if we are working with a valid user or not.
    • If a user phone is found for the given user.id, then render/internal redirect to the Security Questions page
    • If a user.id is not found in the HTTP Session or a user phone is not found that matches the verification code for the given user.id, then redirect the user back to the Verification Code entry page with an error stating "Incorrect verification code. You have 4 more attempts."
      • Insert an ActionLog record of "Reset Password: Verification Code Attempt" with the username in a discrete field.
  7. Security Questionnaire page
    • Upon successful entry of the verification code, display the Security Questionnaire page
    • This page cannot be accessed directly via URL, but only accessible by internal redirect/render by the Verification Code step.
    • If no user is found within the session, then redirect to the login page with no error message
      discrete field.
    • Verify that a User.id is loaded within the session
      • Lookup the user object if not already loaded
    • Check for a HTTP session attribute that states that all other steps in the process have been completed successfully for the given user (user.id, verification code, etc.)
    • Display all security questions associated with the user's profile (should be limited to 5)
    • Display "password" input boxes for each security question (NEVER have multiple choice), not to exceed 255 characters
    • Validate that each security question is answered before the page can be submitted
  8. Submit Security Questions page
    • If no user is found within the session, then redirect to the login page with no error message
    • Compare each security question answer with the encoded value from User's security question answer table
      • Security question answers MUST be password encoded in the database
    • If any one question is wrong, then redisplay the Security Questionnaire page with a message stating that "One or more of the answers are incorrect. 4 attempts remaining"
      • A total of 5 attempts are allowed.
      • Keep track of attempts within the Session
      • Update the error message with a decremented count after each failed attempt
      • Make sure that the first time the user has the security questions display, that the count is reset to 0.
    • If the number of failed attempts == 5, then clear the HTTP session of the User and redirect the user to the
    • If all of the security question answers are correct, then internally redirect to the Password Reset page with an error message stating "Security question failed attempts exceeded the limit. Password reset has been disabled for 20 minutes"
  9. Password reset page
    • Upon successful validation of the security question answers, display the password reset page
    • This page cannot be accessed directly via URL, but only accessible by internal redirect/render by the Security Questionnaire save/validation step.
    • Display a "Password" input field with an input type of "password"
    • Display a "Confirm Password" input field with an input type of "password"
    • Display a "Cancel" button that will redirect the user back to the Login page (preserving their original request parameters so that they can complete the OAuth login workflow)
    • Display a "Reset Password" button that will save the new password
      • Clicking this button will verify that the Password and Confirm Password match using javascript on the page
      • Clicking this button will POST the password to the save password server action
  10. Reset password action
    • Check for a HTTP session attribute that states that all other steps in the process have been completed successfully for the given user (user.id, verification code, security questions, etc.)
      • If any of the attributes required to access this page are not found, then redirect to the login page with no error message
    • Verify/load the user from the user.id in the http session
    • Compare the given Password with the Confirm Password to ensure they match exactly.
      • If the passwords do not match, then redirect back to the "Reset Password Page" with an error message that the Password and Confirm Password do not match.
      • Allow the user to retry as many times as they require
    • Hash encode the Password field and save to the User record in the database
    • Redirect the user to the Login page with a success message of "Password reset successful." (preserving the original client request parameters from the /oauth/authorize request)
  11. CSRF Tokens on every page with a form
    • Every page must have CSRF tokens for the password reset and Login page functionality
    • Enabling Spring CSRF tokens might not work unless they can be limited to specific URL paths (and not for the web services /api).
      • Might need to create a custom CSRF token process for this workflow.

NOTE: Whenever we display a FORM, we need to include some sort of "token" in the session that allows the user to proceed to the next step in the reset password workflow. We must prevent an attacker from entering a valid username + phone and then simply posting the password reset FORM without going through the security questions. There should be some value in the HTTP session that notifies the next workflow step that the prior step was completed successfully.

NOTE: Make sure the ActionLog.username field has an index on it so that lookups on username are performant

References

Match create role endpoint to Java

  • Identify and change our endpoint to match java api
  • If your find that leads to question java api please discuss before making changes to our code

Action Logging: Exclude resources from action logging

Provide the ability to exclude certain URL resources from being logged. These resources might include unrestricted images, stylesheets, javascript files, or other static content that is not pertinent to user data or activity.

Requirements

  • Store a list of resource paths in an external property config that should be EXCLUDED from action logging.
    • Example using AntPath matcher: ['/resources/images/', '/static/css/']
  • Examine the incoming request URL and match the URL against the list of excluded resources
    • If the URL matches an excluded resource address, then skip the action logging.
    • If no match, then log the incoming request/response

Security: Verify Security Code

HTTP POST /api/v1/verify

A secure web service endpoint that requires the user in need of verification to be authenticated by either a username/password or through security questions. Invoking the /verify POST web service requires that the body contains the code delivered to the user's mobile phone via SMS. The code provided in the web service will be verified against the code stored in the User's account.

Post Body:

{ code: 53432 }

Possible Results:

HTTP 204 No Content
HTTP 400 Bad Request

Security: User Verification

Implement an API user verification process as outlined within the Security documentation at
https://github.com/lssinc/voyage-api-java/blob/master/readme_docs/SECURITY.md#user-verification

API Endpoints

  • /v1/verify/send GET
  • sends a unique code to up to 5 mobile phones from the authenticated user profile
    no parameters
  • requires the user to be authenticated
  • no permissions besides being authenticated
  • /v1/verify POST receives a single body param: "code"
    looks up the "code" against the authenticated user's mobile phone codes. If one matches, then the phone is "validated" and the user account is considered "validated"
  • user.is_verify_required = true/false
  • user_phone.verify_code = code value
  • user_phone.verify_code_expires_on = date
  • user_phone.is_validated = true/false
  • JSON encoded {"code":"234324"}
  • requires the user to be authenticated no permissions besides being authenticated

Move authorization server out from resource server

Authorization Server

  • We will be using our existing project as authorization server
  • We will need to identify all share code and move it to common or server
  • Authorization server should have core dependency

Resource Server

  • Create new OWIN project web api2
  • Secure it with JWT token base
  • Resource server should share same dependency as authorization server it could be same or less

Create SSO authentication

  • Users should be able to log in using sso such localhost/sso
  • Implement SSO service endpoint for windows users

Security: Implicit Authentication

Implicit authentication should work the same way as Java

Overview

The user instructs the client 'app' to make API requests on the user's behalf.
The client initiates the authentication using their client ID, but does not provide a password because the user will be required to enter their own username and password to authorize the client.
The API will load both the Client and User objects into the session
This authentication method is the preferred method for a web or mobile app
Web Service: http://localhost:8080/oauth/authorize

Reference: Docs

used for Implicit Authentication

  • accepts the Client ID, redirect url, and few other params
  • redirects a server-side user login form
    upon successful authentication of the user and client, a redirect back to the caller with the access token.
  • returns a JWT bearer token that expires within a few hours

Encrypt access token

Encrypt access token with JWE

  • After user/client is authenticated we will need to encrypt access token user JWE
  • Resource server will still need to be able to verify token for the obvious reason :)

Security: Add Active Directory / LDAP OAuthProvider

Create new authentication provider

  • Windows auth must be able to co-exist with token based auth
  • Inherit OAuthAuthorizationServerProvider call it something like ADAuthenticationProvider
  • Overridden GrantResourceOwnerCredentials
  • Use PrincipalContext to authenticate user against a given domain
  • Use userName and password from OAuthGrantResourceOwnerCredentialsContext
  • Create new OAuthAuthorizationServerOptions and give Provider new ADAuthenticationProvider and set other properties appropriately
  • Test new provider using Postman
  • Create Unit tests for new provider

Create resource server project

Resource Server

  • Move resource server specific feature from our current project into new project
  • Create new host in our IIS server to host it

Document: Create send/verify code section

Send/Verify code section

  • Create new documentation section called "Security"
  • Add new section under Security called Security Features
  • This section description can be taken from here :) Link

Match profile endpoint to Java

Going through .Net code base and identity Api differences

  • Find and change Uri, Request, Response and Permission to match both Java and .Net

Database: Create local sql express and add to our solution

** New local sql express **

Note: Change connection string to localDb and validate with multiple version of visual studio

  • Add sql express to our database project
  • Modify all connection string to point to it
  • Make sure all tests still pass :)
  • After deploy please test if this is still working in our qa server

Security: Use public/private key for signing JWT token

Siging JWT

  • Create public/private key if not existing in key store
  • Create JWT signing service that provides signing certificate
  • Apply signing cert to JWT token generator
  • Use public/private key to sign credential when validating JWT on incoming requests

Security: Send verify code

HTTP GET /api/v1/verify/send

A secure web service endpoint that requires the user in need of verification to be authenticated by either a username/password or through security questions. Invoking the /verify/send web services initiates the delivery of a verification code to the mobile phone on the user's account. The code that is delivered will be a 6-digit code that will be stored securely in the database using the same password hashing method (bcrypt). A mobile phone is required at account creation.

Parameters: none

Possible Results

HTTP 204 No Content

Create Angular 2 web client

New client app

  • This client will be using explicit authentication
  • User Angular 2 CLI
  • User google material design
  • After authenticated with our auth server users should be redirected to a page that show their roles. Use /api/v1/roles endpoint
  • Create basic navigation
  • Create app footer
  • This app will be responsive app
  • If users navigate to landing page with expired token redirect to auth server

Security: Brute Force Attack: Lock OAuth2 Client Account After X Attempts

A brute force attack (BFA) is when an attacker makes thousands of attempts on an HTTP resource in an attempt to guess common values for an input. For example, guessing a username and password on a login page. In most cases there are many combinations of usernames and passwords, so the attacker needs to be able to execute as many combinations as possible per second to find matches (i.e. 3-5k+).

For certain apps, the OAuth2 client account should be locked after a number of failed attempts so that the user's data is protected if the failures are from an attacker. There are app scenarios where locking an OAuth2 client account would disrupt a legitimate client's ability to facilitate access to the API, so it's up to the app owner if they wish to enable this feature. Either way, it's important that the API provide the means to lock OAuth2 client accounts after a specific number of failed login attempts.

Requirements:

  • Store a 'enabled' true/false property in an external property config so that this feature can be turned on/off for any given server and any time.
  • Store a 'max login attempts' property in an external property config so that each app can determine the count that is appropriate
  • Create an event listener or http filter that monitors failed FORM login attempts
  • Create an event listener or http filter that monitors failed BASIC AUTH login attempts
  • Lock OAuth2 client feature approach
    • If the feature is enabled (based on external config property), then execute
    • Lookup the OAuth2 Client by the client identifier provided
    • If the Client exists, then increment the number of failed login attempts (the Client should have a "failedLoginAttempts" property stored on the Client object
    • Check if the client's new failedLoginAttempts > max failed login attempts (from property config), then lock the Client account (client.isLocked = true or client.isEnabled = false)
      • The client should have a locking or disable feature that would prevent further login
      • There is NO forgot password for clients, so the only way to unlock/enable a client account is for the administrator or DBA to manage this change.

Add phone number as part of user account creation

Require phone number

  • Add new field in RegistrationModel.cs call PhoneNumber as string type
  • Add validation rule in RegistrationModelValidator.cs for this field. For now just require rule
  • Update RegisterAsync method in UserService.cs add phone number field to ApplicationUser class UserPhone collection and set PhoneNumberConfirmed to true

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.