Finding housing in NYC is hard. Everyone knows that. But what not everyone knows is that it is easier to find the last four digits of someone’s Social Security number than an apartment…
I was applying for a lease on Beam Living, a Blackstone portfolio company. I went through the normal flows, but (as a security-conscious individual) I always have my network tab open as I browse the web to make sure I am not putting my sensitive information into a website that a script kiddie (or GLM-5.2) could easily break into.
As I was submitting my Social Security number, I figured I should check out the GraphQL (rip, used to be the hot thing) queries that were processing it.
The GraphQL query
At first, I didn’t see anything of note. But when I went to my main profile, I saw a call to pd-dlcore.beamliving.com/graphql with the payload:
View the full GraphQL query query contact ( $contactId : String !) { contact ( contactId : $contactId ) { ... contactInfo __typename } } fragment contactInfo on ContactDtoModel { incomeVerificationMethod incomeCheckStatus incomeCheckReferenceId creditScore address applicationStatus city companyOrSchool consentDate consentIp country dateOfBirth emailAddress emergencyContact { emergencyContactEmail emergencyContactName emergencyContactPhone emergencyContactRelationship __typename } firstName id identityVerificationSubmittedDate incomeCheckDate isCurrentLeasee jobTitle lastName noSsn occupation pets { birthDay breed id isServiceDog licenseNumber name rabiesExpirationDate weight __typename } postalCode preferredName screeningFeePaid ssnInfo state status { ... applicationStatus __typename } telephone __typename } fragment applicationStatus on StatusDtoModel { applicationProgress { basicInfo { actionRequired __typename } dogsInformation { actionRequired __typename } emergencyContact { actionRequired __typename } identityVerification { actionRequired __typename } incomeVerification { actionRequired __typename } payments { actionRequired __typename } review { actionRequired __typename } __typename } progressDetails { applicationForm { description status __typename } firstMonthRent { description status __typename } identityVerification { description identityVerificationCompletedDate identityVerificationStatus identityVerificationSubmittedDate status __typename } incomeVerification { description status __typename } leaseGuarantee { description status __typename } leaseSigning { description signatureId status __typename } managerReview { managerReview managerReviewDecisionCode status __typename } securityDeposit { description paymentLink securityReplacement __typename } backgroundScreeningCheck { status __typename } creditCheck { status __typename } __typename } guestCardCreated unitReservationFailed __typename }
While not necessarily bad, supplying a user’s email into a GraphQL query like this, as opposed to just deriving it from the session cookie, is always a smell.
The obvious test
So I did the obvious thing – I took a friend’s email (I knew he had also used the service) and… boom. The last four digits of his Social Security number, date of birth, home address, IP address, phone number, etc., etc.
The impact was not limited to my application or my building. Beam Living used this leasing portal across its communities:
... continue reading