Skip to content

Commit

Permalink
added hash to share-link for security (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
e-for-eshaan authored Dec 12, 2023
1 parent 3c883b3 commit c5537f2
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 17 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
},
"dependencies": {
"@sentry/nextjs": "^7.86.0",
"@types/bcryptjs": "^2.4.6",
"@vercel/og": "^0.5.20",
"archiver": "^6.0.1",
"aws-sdk": "^2.1515.0",
"axios": "^1.6.2",
"bcryptjs": "^2.4.3",
"chalk": "^5.3.0",
"date-fns": "^2.30.0",
"file-saver": "^2.0.5",
Expand Down
9 changes: 4 additions & 5 deletions src/components/ShareButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@ const defaultText = 'Check out my GitHub Unwrapped of 2023!';

export const ShareButton: React.FC<ShareButtonProps> = ({
callBack,
userName,
imageName,
imageUrl,
className
}) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [tweetText, setTweetText] = useState('');

const domain = window.location.origin;
const imageUrl = `${domain}/shared/${userName}/${imageName}`;

const completeUrl = `${domain}${imageUrl}`;
const shareToTwitter = () => {
const encodedText = encodeURIComponent(tweetText || defaultText);
const encodedImageUrl = encodeURIComponent(imageUrl);
const encodedImageUrl = encodeURIComponent(completeUrl);

const twitterShareUrl = `https://twitter.com/intent/tweet?text=${encodedText}&url=${encodedImageUrl}`;

Expand All @@ -55,7 +54,7 @@ export const ShareButton: React.FC<ShareButtonProps> = ({
className="cursor-pointer"
onClick={toggleMenu}
/>
<CopyPaperClip textToCopy={imageUrl} />
<CopyPaperClip textToCopy={completeUrl} />
</div>
{isMenuOpen && (
<div className="absolute md:w-96 w-72 md:right-12 right-4 top-12 bg-[#11142e] rounded-md shadow-lg p-4">
Expand Down
10 changes: 2 additions & 8 deletions src/components/SwiperCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ShareButton } from './ShareButton';
import { UpdatedImageFile } from '@/types/images';
import { CardTypes, sequence } from '@/types/cards';
import { track } from '@/constants/events';
import { extractFilenameWithoutExtension } from '@/utils/stringHelpers';

interface SwiperCarouselProps {
images: UpdatedImageFile[];
Expand Down Expand Up @@ -92,6 +93,7 @@ const SwiperCarousel: React.FC<SwiperCarouselProps> = ({
<ShareButton
userName={userName}
imageName={extractFilenameWithoutExtension(image.fileName)}
imageUrl={image.url}
className="share-active-image cursor-pointer"
callBack={() => {
singleImageSharingCallback({ images: image });
Expand Down Expand Up @@ -135,11 +137,3 @@ const sortImageCards = (imageA: UpdatedImageFile, imageB: UpdatedImageFile) => {

return indexOfA - indexOfB;
};

const extractFilenameWithoutExtension = (input: string): string => {
const parts = input.split('/');
const filenameWithExtension = parts[parts.length - 1];
const filenameParts = filenameWithExtension.split('.');
const filenameWithoutExtension = filenameParts[0];
return filenameWithoutExtension || '';
};
2 changes: 2 additions & 0 deletions src/constants/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ export const cardColorsMap = {
green: '#71CB7F',
midnight: '#442773'
};

export const HASH_LENGTH = 6;
19 changes: 15 additions & 4 deletions src/pages/api/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { dec } from '@/api-helpers/auth-supplementary';
import { fetchSavedCards, saveCards } from '@/api-helpers/persistance';
import { fetchUser } from '@/api-helpers/exapi-sdk/github';
import { GithubUser } from '@/api-helpers/exapi-sdk/types';
import {
bcryptGen,
extractFilenameWithoutExtension
} from '@/utils/stringHelpers';

const fetchAndDownloadImageBuffer = async (
req: NextApiRequest,
Expand Down Expand Up @@ -60,10 +64,17 @@ const fetchAndDownloadImageBuffer = async (
} else {
res.setHeader('Content-Type', 'application/json');
res.send(
imageBuffer.map(({ data, fileName }) => ({
fileName,
data: `data:image/png;base64,${data.toString('base64')}`
}))
imageBuffer.map(({ data, fileName }) => {
const file = extractFilenameWithoutExtension(fileName);
const username = user.login;
const hash = bcryptGen(username + file);

return {
fileName,
url: `/shared/${username}/${file}/${hash}`,
data: `data:image/png;base64,${data.toString('base64')}`
};
})
);
}
console.log(chalk.green('Successfully sent buffer to client'));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import chalk from 'chalk';
import { NextApiRequest, NextApiResponse } from 'next';
import { fetchSavedCard } from '@/api-helpers/persistance';
import { bcryptGen } from '@/utils/stringHelpers';

const fetchAndDownloadImageBuffer = async (
req: NextApiRequest,
Expand All @@ -9,6 +10,15 @@ const fetchAndDownloadImageBuffer = async (
let username = req.query.username as string;
let cardName = req.query.cardname as string;

const generatedHash = bcryptGen(username + cardName);
const linkHash = (req.query.hash as string[]).join('/') as string;

if (generatedHash !== linkHash) {
return res.status(400).json({
message: 'Invalid parameters, must pass valid hash.'
});
}

if (!username) {
return res.status(400).json({
message: 'Invalid parameters, must pass valid username.'
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions src/types/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export type ImageFile = {
export type UpdatedImageFile = {
fileName: string;
data: string;
url: string;
};
18 changes: 18 additions & 0 deletions src/utils/stringHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import { hashSync } from 'bcryptjs';
import { HASH_LENGTH } from '@/constants/general';

export const capitalize = (sentence?: string): string => {
if (!sentence) return '';
return sentence.replace(/\b\w/g, (match) => match.toUpperCase());
};

export const bcryptGen = (username: string): string => {
const salt = process.env.HASHING_SALT as string;
if (!salt) return '';
const hash = hashSync(username, salt);
return hash.slice(-HASH_LENGTH);
};

export const extractFilenameWithoutExtension = (input: string): string => {
const parts = input.split('/');
const filenameWithExtension = parts[parts.length - 1];
const filenameParts = filenameWithExtension.split('.');
const filenameWithoutExtension = filenameParts[0];
return filenameWithoutExtension || '';
};
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3044,6 +3044,11 @@
dependencies:
"@babel/types" "^7.20.7"

"@types/bcryptjs@^2.4.6":
version "2.4.6"
resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.6.tgz#2b92e3c2121c66eba3901e64faf8bb922ec291fa"
integrity sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==

"@types/eslint@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78"
Expand Down Expand Up @@ -4605,6 +4610,11 @@ batch@0.6.1:
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==

bcryptjs@^2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==

bfj@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b"
Expand Down

0 comments on commit c5537f2

Please sign in to comment.