-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[PM-20578] Encrypt Risk Insights report and send it to server #14659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d4271b4
pm-20577 encrypt and save api
voommen-livefront 9319dbc
PM-20577 updated encryption/decryption
voommen-livefront 585c7b0
PM-20577 fixed failing unit tests
voommen-livefront 09ac1c6
PM-20577 fixed errors
voommen-livefront 45212c2
PM-20577 return a null from getRiskInsightsReport
voommen-livefront bc019ce
PM-20577 remove unwanted encryption
voommen-livefront c2719b4
PM-20577 Retrieve data from cache
voommen-livefront bdb5e9e
PM-20577 replace combineLatest with Zip
voommen-livefront e47bcb2
Merge branch 'main' into dirt/pm-20577/report-summmary-for-db
voommen-livefront ae4ab8a
PM-20577 save report summary to database
voommen-livefront 02e0eb6
PM-20578 fix build errors and update variable types
voommen-livefront 8960acb
PM-20578 retrieve report from DB
voommen-livefront 9ef274a
PM-20578 renamed ReportDecipherService to risk-insights-encryption.seโฆ
voommen-livefront 5107ec2
PM-20578 removed commented liens
voommen-livefront File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
...ense/bit-common/src/dirt/reports/risk-insights/services/risk-insights-api.service.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import { mock } from "jest-mock-extended"; | ||
|
|
||
| import { ApiService } from "@bitwarden/common/abstractions/api.service"; | ||
| import { OrganizationId } from "@bitwarden/common/types/guid"; | ||
|
|
||
| import { SaveRiskInsightsReportRequest } from "../models/password-health"; | ||
|
|
||
| import { RiskInsightsApiService } from "./risk-insights-api.service"; | ||
|
|
||
| describe("RiskInsightsApiService", () => { | ||
| let service: RiskInsightsApiService; | ||
| const apiService = mock<ApiService>(); | ||
|
|
||
| beforeEach(() => { | ||
| service = new RiskInsightsApiService(apiService); | ||
| }); | ||
|
|
||
| it("should be created", () => { | ||
| expect(service).toBeTruthy(); | ||
| }); | ||
|
|
||
| it("should call apiService.send with correct parameters for saveRiskInsightsReport", (done) => { | ||
| const orgId = "org1" as OrganizationId; | ||
| const request: SaveRiskInsightsReportRequest = { | ||
| data: { | ||
| organizationId: orgId, | ||
| date: new Date().toISOString(), | ||
| reportData: "test", | ||
| totalMembers: 10, | ||
| totalAtRiskMembers: 5, | ||
| totalApplications: 100, | ||
| totalAtRiskApplications: 50, | ||
| totalCriticalApplications: 22, | ||
| }, | ||
| }; | ||
| const response = { | ||
| ...request.data, | ||
| }; | ||
|
|
||
| apiService.send.mockReturnValue(Promise.resolve(response)); | ||
|
|
||
| service.saveRiskInsightsReport(request).subscribe((result) => { | ||
| expect(result).toEqual(response); | ||
| expect(apiService.send).toHaveBeenCalledWith( | ||
| "POST", | ||
| `/reports/organization-reports`, | ||
| request.data, | ||
| true, | ||
| true, | ||
| ); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); |
44 changes: 44 additions & 0 deletions
44
...n_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-api.service.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { from, Observable, of } from "rxjs"; | ||
|
|
||
| import { ApiService } from "@bitwarden/common/abstractions/api.service"; | ||
| import { OrganizationId } from "@bitwarden/common/types/guid"; | ||
|
|
||
| import { | ||
| GetRiskInsightsReportResponse, | ||
| SaveRiskInsightsReportRequest, | ||
| SaveRiskInsightsReportResponse, | ||
| } from "../models/password-health"; | ||
|
|
||
| export class RiskInsightsApiService { | ||
| constructor(private apiService: ApiService) {} | ||
|
|
||
| saveRiskInsightsReport( | ||
| request: SaveRiskInsightsReportRequest, | ||
| ): Observable<SaveRiskInsightsReportResponse> { | ||
| const dbResponse = this.apiService.send( | ||
| "POST", | ||
| `/reports/organization-reports`, | ||
| request.data, | ||
| true, | ||
| true, | ||
| ); | ||
|
|
||
| return from(dbResponse as Promise<SaveRiskInsightsReportResponse>); | ||
| } | ||
|
|
||
| getRiskInsightsReport(orgId: OrganizationId): Observable<GetRiskInsightsReportResponse | null> { | ||
| const dbResponse = this.apiService | ||
| .send("GET", `/reports/organization-reports/latest/${orgId.toString()}`, null, true, true) | ||
| .catch((error: any): any => { | ||
| if (error.statusCode === 404) { | ||
| return null; // Handle 404 by returning null or an appropriate default value | ||
| } | ||
| throw error; // Re-throw other errors | ||
| }); | ||
|
|
||
| if (dbResponse instanceof Error) { | ||
| return of(null); | ||
| } | ||
| return from(dbResponse as Promise<GetRiskInsightsReportResponse>); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question โ Will this report be automatically generated and submitted on a schedule, or manually created only? If the former, are we OK with this unencrypted metadata?
Unless not possible, I would recommend encrypting all metadata here. Specifically, reportData should be one blob including both the summary data and detail data. Below, I show a theoretical attack that may abuse the summary data to decrypt all org vault data remotely.
Specifically, the following attacks (non exhaustive, and non validated) may be made possible. I have not implemented or validated them, and they are purely on the basis of reading the code.
Leak individiual item/application weakness status
Before every report generation, the server forces a sync to the client that will create the report, and present a false view of the ciphers, specifically, just show 1 cipher. The total will then reflect just that cipher, and thus the safety presumed by the use of summarized data is broken. This attack can be optimized to be much faster than a linear amount of queries.
Remote predicate evaluation on arbitrary org encrypted strings
Further, it allows the server to remotely run certain predicates such as
findWeakPasswordon any arbitrary org-key encrypted data (not just passwords) and obtain the result, by swapping encrypted strings within the cipher.this.passwordStrengthService.getPasswordStrength(is called, which uses zxcvbn to compare a security score, of the password compared to, including the encrypted "username" field. If the server has access to known plaintext ciphertext pairs here, they may be able to create a decryption oracle capable of remotely decrypting all org vault data with this. NOTE: This is theoretical, I have not validated this thoroughly. Roughly, the attacker would remotely, via the previous attack make the client evaluate password strength. As the password, the server sets the target encstring. As the username, they set one of the known plaintext / ciphertext pairs. zxcvbn will return a different score based on the similarity of the two, and depending on if it reaches a threshold, it will be a cipher at risk. This can be used, given enough plaintext ciphertext pairs to fully recovery the target plaintext, thus making full decryption of all vault data possible. One would find a plaintext-ciphertext pair close to the threshold, then flip each character to figure out which character is the correct character for that position. However, this requires a lot of plaintext ciphertext pairs, or an encryption oracle.Remote predicate evaluation - equality check
Using the same attack as above, the server can perform an equality check on any two arbitrary org-key encrypted org strings, by presenting 2 items, with the target encstrings set as the password.
Remote clustering of encrypted vault items by app
Further, it allows the server to cluster any ciphers and confirm if the are for the same domain. Example: Server provides cipher A, cipher B. If only one app is reported, then both are the same app and get clustered. This can be improved to be much faster than linear time.
(Out of scope note) Clusters / individual weak items can be tied to real domains
Combining this with a compromised icon server (out of scope) would allow tying encrypted ciphers to domains, and thus leak which accounts are weak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will cc @mandreko-bitwarden, @jlf0dev on this, because these (presumed) attacks are security relevant, possibly threatening all of organization vault encryption confidentiality. I'm keeping these comments public because this is not released code, but just draft.
@jlf0dev I have not implemented these attacks because I looked at this as an early code review for the unrelated issue bellow, but it felt unethical not to look closer here. I don't know if it's worth to spend the time to confirm the validity of the attacks above. If we just encrypt the metadata, then all of them are invalid etiher way.