juice-shop

Форк
0
/
insecurity.ts 
201 строка · 7.8 Кб
1
/*
2
 * Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3
 * SPDX-License-Identifier: MIT
4
 */
5

6
import fs from 'fs'
7
import crypto from 'crypto'
8
import { type Request, type Response, type NextFunction } from 'express'
9
import { type UserModel } from 'models/user'
10
import expressJwt from 'express-jwt'
11
import jwt from 'jsonwebtoken'
12
import jws from 'jws'
13
import sanitizeHtmlLib from 'sanitize-html'
14
import sanitizeFilenameLib from 'sanitize-filename'
15
import * as utils from './utils'
16

17
/* jslint node: true */
18
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
19
// @ts-expect-error FIXME no typescript definitions for z85 :(
20
import * as z85 from 'z85'
21

22
export const publicKey = fs ? fs.readFileSync('encryptionkeys/jwt.pub', 'utf8') : 'placeholder-public-key'
23
const privateKey = '-----BEGIN RSA PRIVATE KEY-----\r\nMIICXAIBAAKBgQDNwqLEe9wgTXCbC7+RPdDbBbeqjdbs4kOPOIGzqLpXvJXlxxW8iMz0EaM4BKUqYsIa+ndv3NAn2RxCd5ubVdJJcX43zO6Ko0TFEZx/65gY3BE0O6syCEmUP4qbSd6exou/F+WTISzbQ5FBVPVmhnYhG/kpwt/cIxK5iUn5hm+4tQIDAQABAoGBAI+8xiPoOrA+KMnG/T4jJsG6TsHQcDHvJi7o1IKC/hnIXha0atTX5AUkRRce95qSfvKFweXdJXSQ0JMGJyfuXgU6dI0TcseFRfewXAa/ssxAC+iUVR6KUMh1PE2wXLitfeI6JLvVtrBYswm2I7CtY0q8n5AGimHWVXJPLfGV7m0BAkEA+fqFt2LXbLtyg6wZyxMA/cnmt5Nt3U2dAu77MzFJvibANUNHE4HPLZxjGNXN+a6m0K6TD4kDdh5HfUYLWWRBYQJBANK3carmulBwqzcDBjsJ0YrIONBpCAsXxk8idXb8jL9aNIg15Wumm2enqqObahDHB5jnGOLmbasizvSVqypfM9UCQCQl8xIqy+YgURXzXCN+kwUgHinrutZms87Jyi+D8Br8NY0+Nlf+zHvXAomD2W5CsEK7C+8SLBr3k/TsnRWHJuECQHFE9RA2OP8WoaLPuGCyFXaxzICThSRZYluVnWkZtxsBhW2W8z1b8PvWUE7kMy7TnkzeJS2LSnaNHoyxi7IaPQUCQCwWU4U+v4lD7uYBw00Ga/xt+7+UqFPlPVdz1yyr4q24Zxaw0LgmuEvgU5dycq8N7JxjTubX0MIRR+G9fmDBBl8=\r\n-----END RSA PRIVATE KEY-----'
24

25
interface ResponseWithUser {
26
  status: string
27
  data: UserModel
28
  iat: number
29
  exp: number
30
  bid: number
31
}
32

33
interface IAuthenticatedUsers {
34
  tokenMap: Record<string, ResponseWithUser>
35
  idMap: Record<string, string>
36
  put: (token: string, user: ResponseWithUser) => void
37
  get: (token: string) => ResponseWithUser | undefined
38
  tokenOf: (user: UserModel) => string | undefined
39
  from: (req: Request) => ResponseWithUser | undefined
40
  updateFrom: (req: Request, user: ResponseWithUser) => any
41
}
42

43
export const hash = (data: string) => crypto.createHash('md5').update(data).digest('hex')
44
export const hmac = (data: string) => crypto.createHmac('sha256', 'pa4qacea4VK9t9nGv7yZtwmj').update(data).digest('hex')
45

46
export const cutOffPoisonNullByte = (str: string) => {
47
  const nullByte = '%00'
48
  if (utils.contains(str, nullByte)) {
49
    return str.substring(0, str.indexOf(nullByte))
50
  }
51
  return str
52
}
53

54
export const isAuthorized = () => expressJwt(({ secret: publicKey }) as any)
55
export const denyAll = () => expressJwt({ secret: '' + Math.random() } as any)
56
export const authorize = (user = {}) => jwt.sign(user, privateKey, { expiresIn: '6h', algorithm: 'RS256' })
57
export const verify = (token: string) => token ? (jws.verify as ((token: string, secret: string) => boolean))(token, publicKey) : false
58
export const decode = (token: string) => { return jws.decode(token)?.payload }
59

60
export const sanitizeHtml = (html: string) => sanitizeHtmlLib(html)
61
export const sanitizeLegacy = (input = '') => input.replace(/<(?:\w+)\W+?[\w]/gi, '')
62
export const sanitizeFilename = (filename: string) => sanitizeFilenameLib(filename)
63
export const sanitizeSecure = (html: string): string => {
64
  const sanitized = sanitizeHtml(html)
65
  if (sanitized === html) {
66
    return html
67
  } else {
68
    return sanitizeSecure(sanitized)
69
  }
70
}
71

72
export const authenticatedUsers: IAuthenticatedUsers = {
73
  tokenMap: {},
74
  idMap: {},
75
  put: function (token: string, user: ResponseWithUser) {
76
    this.tokenMap[token] = user
77
    this.idMap[user.data.id] = token
78
  },
79
  get: function (token: string) {
80
    return token ? this.tokenMap[utils.unquote(token)] : undefined
81
  },
82
  tokenOf: function (user: UserModel) {
83
    return user ? this.idMap[user.id] : undefined
84
  },
85
  from: function (req: Request) {
86
    const token = utils.jwtFrom(req)
87
    return token ? this.get(token) : undefined
88
  },
89
  updateFrom: function (req: Request, user: ResponseWithUser) {
90
    const token = utils.jwtFrom(req)
91
    this.put(token, user)
92
  }
93
}
94

95
export const userEmailFrom = ({ headers }: any) => {
96
  return headers ? headers['x-user-email'] : undefined
97
}
98

99
export const generateCoupon = (discount: number, date = new Date()) => {
100
  const coupon = utils.toMMMYY(date) + '-' + discount
101
  return z85.encode(coupon)
102
}
103

104
export const discountFromCoupon = (coupon: string) => {
105
  if (coupon) {
106
    const decoded = z85.decode(coupon)
107
    if (decoded && (hasValidFormat(decoded.toString()) != null)) {
108
      const parts = decoded.toString().split('-')
109
      const validity = parts[0]
110
      if (utils.toMMMYY(new Date()) === validity) {
111
        const discount = parts[1]
112
        return parseInt(discount)
113
      }
114
    }
115
  }
116
  return undefined
117
}
118

119
function hasValidFormat (coupon: string) {
120
  return coupon.match(/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[0-9]{2}-[0-9]{2}/)
121
}
122

123
// vuln-code-snippet start redirectCryptoCurrencyChallenge redirectChallenge
124
export const redirectAllowlist = new Set([
125
  'https://github.com/juice-shop/juice-shop',
126
  'https://blockchain.info/address/1AbKfgvw9psQ41NbLi8kufDQTezwG8DRZm', // vuln-code-snippet vuln-line redirectCryptoCurrencyChallenge
127
  'https://explorer.dash.org/address/Xr556RzuwX6hg5EGpkybbv5RanJoZN17kW', // vuln-code-snippet vuln-line redirectCryptoCurrencyChallenge
128
  'https://etherscan.io/address/0x0f933ab9fcaaa782d0279c300d73750e1311eae6', // vuln-code-snippet vuln-line redirectCryptoCurrencyChallenge
129
  'http://shop.spreadshirt.com/juiceshop',
130
  'http://shop.spreadshirt.de/juiceshop',
131
  'https://www.stickeryou.com/products/owasp-juice-shop/794',
132
  'http://leanpub.com/juice-shop'
133
])
134

135
export const isRedirectAllowed = (url: string) => {
136
  let allowed = false
137
  for (const allowedUrl of redirectAllowlist) {
138
    allowed = allowed || url.includes(allowedUrl) // vuln-code-snippet vuln-line redirectChallenge
139
  }
140
  return allowed
141
}
142
// vuln-code-snippet end redirectCryptoCurrencyChallenge redirectChallenge
143

144
export const roles = {
145
  customer: 'customer',
146
  deluxe: 'deluxe',
147
  accounting: 'accounting',
148
  admin: 'admin'
149
}
150

151
export const deluxeToken = (email: string) => {
152
  const hmac = crypto.createHmac('sha256', privateKey)
153
  return hmac.update(email + roles.deluxe).digest('hex')
154
}
155

156
export const isAccounting = () => {
157
  return (req: Request, res: Response, next: NextFunction) => {
158
    const decodedToken = verify(utils.jwtFrom(req)) && decode(utils.jwtFrom(req))
159
    if (decodedToken?.data?.role === roles.accounting) {
160
      next()
161
    } else {
162
      res.status(403).json({ error: 'Malicious activity detected' })
163
    }
164
  }
165
}
166

167
export const isDeluxe = (req: Request) => {
168
  const decodedToken = verify(utils.jwtFrom(req)) && decode(utils.jwtFrom(req))
169
  return decodedToken?.data?.role === roles.deluxe && decodedToken?.data?.deluxeToken && decodedToken?.data?.deluxeToken === deluxeToken(decodedToken?.data?.email)
170
}
171

172
export const isCustomer = (req: Request) => {
173
  const decodedToken = verify(utils.jwtFrom(req)) && decode(utils.jwtFrom(req))
174
  return decodedToken?.data?.role === roles.customer
175
}
176

177
export const appendUserId = () => {
178
  return (req: Request, res: Response, next: NextFunction) => {
179
    try {
180
      req.body.UserId = authenticatedUsers.tokenMap[utils.jwtFrom(req)].data.id
181
      next()
182
    } catch (error: any) {
183
      res.status(401).json({ status: 'error', message: error })
184
    }
185
  }
186
}
187

188
export const updateAuthenticatedUsers = () => (req: Request, res: Response, next: NextFunction) => {
189
  const token = req.cookies.token || utils.jwtFrom(req)
190
  if (token) {
191
    jwt.verify(token, publicKey, (err: Error | null, decoded: any) => {
192
      if (err === null) {
193
        if (authenticatedUsers.get(token) === undefined) {
194
          authenticatedUsers.put(token, decoded)
195
          res.cookie('token', token)
196
        }
197
      }
198
    })
199
  }
200
  next()
201
}
202

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.