Security recommendations
This page outlines recommendations for teams to follow when using GitHub to develop software. The recommendations are split into three different categories:
✅ Minimum - These are the baseline security controls that should be implemented by all teams. These help protect against common threats and are fairly easy to implement. They should be implemented for all projects regardless of size or risk level.
⭐ Recommended - These are additional security controls to be applied on top of the minimum controls. If you’re working on a project that is sensitive or supports critical business services, you should be implementing these in your project.
🏆 Advanced - These are advanced security controls to be implemented by mature teams for the most security-conscious projects. They should be implemented once both the minimum and recommended requirements are in place.
Teams should be applying the minimum security controls to all projects. The recommended and advanced security controls are circumstantial and depend on the nature of the project. Teams should aim to implement these in their project if applicable.
🪪 Authentication
✅ Minimum: Enable MFA for your GitHub account
Why it matters
Using multi-factor authentication (MFA), also known as two-factor authentication, helps to secure your GitHub account by adding an extra layer of security beyond just a password. Passwords can be easily stolen through phishing, data breaches or malware. MFA helps prevent unuathorised access in these scenarios. It’s also strongly recommended by GitHub.
How to implement
Follow the instructions at Configuring two-factor authentication.
When choosing a two-factor authentication method, use the time-based one-time password (TOTP) app if possible. This is much more secure than using SMS.
✅ Minimum: Use fine-grained personal access tokens over classic personal access tokens
Why it matters
Classic personal access tokens (PAT) give access to all repositories a user account has access to. This increases the blast radius if the PAT is compromised. They also have less granular permissions compared to fine-grained PATs. Fine-grained PATs allow you to specify exactly which repositories, organisations and permissions the token can access. This limits the blast radius in the event the PAT is compromised. They also benefit from improved auditing and management.
How to implement
Follow the instructions at Creating a fine-grained personal access token. When creating the PAT, follow the best practises below:
- Give the PAT access to only specific repositories. Avoid giving the PAT access to all repositories.
- Choose the minimal permissions necessary for the PAT. The GitHub REST API documentation provides the necessary permissions for each endpoint.
- Choose the shortest expiration date possible.
- Do not store the PAT in code or config files. Store it in a Password Manager or as a GitHub Action secret.
At the time of writing, not all GitHub resources support fine-grained PATs. If the resource you want to access doesn’t support fine-grained PATs, use a classic PAT until support is added.
🏆 Advanced: For automations use a GitHub App for authentication
Why it matters
When running automations within GitHub such as CI/CD, integrations, and administration tasks, these should run under a non-human identity rather than a human identity. If using a human identity and the user left leave Imperial, their access would be removed and the automation would cease to function.
A common approach for running automations under a non-human identity is to create a bot account on GitHub. This is effectively a GitHub account that isn’t tied to a particular user but a team. It has an email and password that is shared across a team allowing multiple users to manage the account. A PAT is created under the account and is used for running the automation. This approach is reasonable but has a number of limitations:
- PATs are long-lived (unless manually rotated) and, if leaked, can be misused until revoked
- PATs expire after a set length of time and need regenerating. If the PAT is not regenerated, automations may stop working
- Can be difficult to maintain at scale across multiple repositories and organisations
- Making changes often requires logging into the bot account which can be cumbersome
GitHub Apps reduce these limitations by providing a secure and scalable method for running automations. GitHub Apps can act independently of a user. Rather than creating a GitHub account, you create a GitHub App and grant the app access to the required resources. For authentication, the GitHub App uses a private key to generate a short-lived JSON Web Token (JWT) and installation access token. These can then be used to authenticate requests to the API. This removes the need to regularly regenerate PATs and manage non-human GitHub accounts.
How to implement:
Review the instructions at Creating GitHub apps.
GitHub Apps must be created and managed by an organisation owner. For that reason, they’re only recommended for advanced use cases.
🔒 Role-Based Access Control (RBAC)
✅ Minimum: Limit access to approved Imperial staff, students and third parties
Why it matters
Limiting access to approved Imperial staff, students, and third parties helps ensure that sensitive systems and data are only accessible to individuals who have a legitimate reason to use them. This reduces the risk of accidental data exposure, insider threats, and unauthorised access by former employees or external actors. By enforcing strict access boundaries, Imperial can maintain accountability, comply with data protection regulations, and uphold the integrity of its digital infrastructure.
How to implement
Imperial staff and students should first be added as a member of the ImperialCollegeLondon organisation. Instructions on how to do this can be found at gaining access to the Imperial College London organisation. Once they’re a member of the organisation, they can be added to the repository under “Collaborators and teams” in the repository settings.
Other 3rd parties (e.g. contractors, consultants, researchers etc.) should also be added as a member of the organisation if they have an Imperial account. If they don’t have an Imperial account, they can be added as an outside collaborator following the instructions at Add an outside collaborator to my GitHub repository.
If a user no longer requires access to a repository, they should be removed.
✅ Minimum: Ensure privileged roles such as admin and maintainer are restricted
Why it matters
Privileged roles such as Admin and Maintainer have elevated access that can significantly impact the security, stability, and integrity of systems and codebases. If these roles are assigned too broadly or without oversight, they increase the risk of accidental misconfigurations, unauthorised changes, or malicious actions. Restricting privileged roles to only those who genuinely need them helps enforce the principle of least privilege, reduces the attack surface, and ensures that critical operations are performed by trusted and accountable individuals.
How to implement
- Carefully review the permissions for each role at Repository roles for an organisation and select the most appropriate role for the user.
- Limit Admin access to only a handful of people who should be able to perform destructive actions such as deleting a repository.
- Limit Maintainer access to only users who need to manage the repository settings.
⭐ Recommended: Only add organisation members to repositories not outside collaborators
Why it matters
Restricting repository access to organisation members rather than using outside collaborators ensures that only individuals with a valid Imperial account can access internal resources. Because organisation membership is tied to SAML Single Sign-On (SSO), access is automatically revoked when a user’s Imperial account is deactivated (e.g. when they leave the university). This provides a strong access control mechanism, reduces the risk of lingering or orphaned accounts, and ensures that access is always aligned with current institutional affiliation and identity verification.
How to implement
When assigning a user access to a repository, ensure the account you’re adding is a member of the organisation and not an outside collaborator.
If the user isn’t a member of the organisation, they should first be added by following the instructions at gaining access to the Imperial College London organisation. If they don’t have an Imperial account, one should be created for them.
⭐ Recommended: Use GitHub Teams for assigning permissions
Why it matters
Using GitHub Teams to assign repository permissions simplifies access management, improves visibility, and ensures consistency across projects. Instead of managing individual user access on each repository, teams allow you to group users by role or function (e.g., developers, reviewers, maintainers) and assign permissions at scale. This makes it easier to onboard new members, audit access, and enforce the principle of least privilege. It also aligns well with organisational structures and reduces the risk of misconfigured or forgotten permissions.
How to implement
Create teams that reflect functional roles or responsibilities. For example:
Team Name | Users | GitHub Role |
---|---|---|
ProjectA - Developers | Alice, Bob, Charlie | Write |
ProjectA - Product Owners | Diana, Eric | Read |
ProjectA - Maintainers | Frank | Maintain |
ProjectB - Tech Leads | Grace, Henry | Admin |
Once the teams have been created, you can assign the team to a particular repository and give it a role. This also helps repository navigation as users can view all repositories a team has access to under the team view.
🏆 Advanced: Use a codeowners file to define who is responsible for certain code in a repository
Why it matters
A CODEOWNERS file defines who is responsible for specific parts of a codebase, ensuring that the right people are automatically requested for review when changes are made. This improves accountability, speeds up the review process, and helps maintain code quality by involving subject matter experts. It also reduces the risk of unreviewed or misreviewed changes being merged, especially in large or multi-team repositories. By clearly assigning ownership, teams can enforce standards, and streamline collaboration.
How to implement
-
Decide who is responsible for each part of the codebase. For example, you may have a frontend and backend within the same repository. TeamA could be responsible for the frontend and TeamB for the backend.
-
Follow the instructions at About code owners to create a codeowners file.
-
Create a new branch ruleset under repository settings and ensure the following options are enabled:
-
When a new pull request changes code that matches a pattern in the
CODEOWNERS
file, those users/teams are automatically requested as reviewers
📦 Dependency Management
✅ Minimum: Enable Dependabot Alerts
Why it matters
GitHub Dependabot is a security feature that helps keep the software dependencies in your project up-to-date. The National Cyber Security Centre recommends applying updates as soon as possible, and ideally automatically. Dependabot can help with this by monitoring the dependencies in your repository and raising alerts for vulnerabilities. If a vulnerability is identified, it can automatically raise a pull request to update the dependency. It can also raise pull requests every time a new version of a dependency is available.
How to implement
-
Check your project uses a package manager that is supported by Dependabot. Dependabot supports a variety of package managers. These include Gradle, Maven, pip, npm, NuGet and many more. You can view a full list at Dependabot supported ecosystems and repositories.
-
Follow the instructions at Managing Dependabot alerts for you repository to turn on Dependabot alerts. Once enabled, you can view vulnerabilities under the security tab.
⭐ Recommended: Enable Automatic Dependency Updates
Why it matters:
Automatically updating dependencies ensures your project stays current with the latest security patches and improvements, reducing manual effort and the risk of outdated packages.
Rather than manually upgrading package versions, Dependabot does this for you and automatically creates a pull request targeting your default branch. The behaviour of Dependabot can be heavily customised according to the project’s needs.
How to implement:
Follow the instructions at Enabling Dependabot version updates.
🏆 Advanced: Consider Renovate if experiencing limitations with Dependabot
Why it matters:
While Dependabot is effective for many common dependency updates, it has limitations - particularly with monorepos, custom registries, or less common ecosystems. Renovate offers greater flexibility and configurability, making it a powerful alternative when Dependabot cannot fully meet your project’s needs. It supports a wider range of package managers, allows for fine-grained scheduling and grouping of updates, and can be tailored to complex workflows. Using Renovate ensures that all dependencies, not just the ones Dependabot supports, are kept up to date - reducing security risks and maintenance overhead.
Renovate is an open-source project and can be installed on repositories for free. It has two main hosting methods - GitHub App (hosted by mend.io - the company behind Renovate) or self-hosted. If you’re cautious of allowing mend.io access to your repository, Imperial provide a self-hosted instance of Renovate. Simply create an issue under the self-hosted-renovate repository and the GitHub Admins will assist you in setting it up.
How to implement:
- Choose whether to use the GitHub App (hosted by mend.io) or self-hosted version of Renovate.
- Checkout Renovate’s reading list to understand how it works and key concepts.
- Configure Renovate as per the options available at Renovate configuration overview.
🗝️ Managing Secrets
✅ Minimum: Do not store secrets in code or config files
Why it matters
In GitHub’s 2024 Octoverse report, 39 million secret leaks were detected in GitHub repositories. According to the IBM Cost of Data Breach Report (2024), breaches involving compromised credentials cost organisations an average of $4.88 million per incident. This is a 10% increase from the previous year. Good secrets management is important as it helps organisations prevent security threats that may arise through exposed credentials. If exposed, secrets can grant adversaries unauthorised access to an organisation’s code base, databases and other sensitive data. This is of particular concern in public repositories where attackers can, and do, regularly scan for leaked secrets.
How to implement
- Before creating a commit, read through the proposed changes and check for any secrets
- Use a dedicated secrets management solution to store and access secrets securely. This could be Azure Key vault, GitHub Actions secrets or a password manager.
⭐ Recommended: Use code scanning tools to identify and block secrets from being committed
Why it matters
Code scanning tools use pattern matching to detect secrets such as API keys, passwords, and tokens. This helps prevent sensitive information from being accidentally committed to version control. These tools act as a safety net, catching mistakes before they become security incidents. By scanning code in real time (pre-commit) or during CI/CD workflows, they reduce the risk of credential leaks, unauthorised access, and compliance violations.
They also support incident response by alerting teams to exposures early, allowing for quick remediation and secret rotation. Remediating a secret after it’s been pushed to GitHub is much more labour intensive then catching it pre-commit. Implementing secret scanning is a proactive and automated way to improve security for a project.
How to implement
For public repos, you should enable GitHub Secret Scanning. This is available free of charge for public repositories. To enable secrets scanning, navigate to the “Advanced Security” tab within the repository settings and enable the following options: