Skip to content
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

fix: pregenerate https certificate #6173

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
369 changes: 1 addition & 368 deletions packages/vite/LICENSE.md

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions packages/vite/dev-cert.pem
@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify: is it safe to commit a private key to repo?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a key that will only be used during development, and to trick the browser, it isn't a secure HTTPS certificate. But what we currently do isn't secure either. I'm still not sure if this is possible, but at least with the people we have discussed so far, it looks like inlining the cert is the same as generating it locally with fixed params.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TrySound should we add a comment in the .pem so people finding this private key in our repo would not be surprised? Looks like anything before the -----BEGIN should be ignored by parsers: https://stackoverflow.com/questions/19578812/comments-in-a-pem-file
Seems there are some parsers that have issues but it may be worth trying. Or any other idea to avoid getting security complaints once we merge this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really needed at all? i'd rather not put a private key into a public repo for whatever reason. Someone takes it and uses it to target developers using vite in a social engineering/phishing attack.
Even without a public private key, a centralized cert is a bad idea imho. If you want to do this, generate the whole thing on the client side and preferably throw away the key afterwards so no other certs can be created.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominikg the same answer I gave @patak-dev privately in discord

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominikg aren't we doing the same already with selfsigned with static params? I was under the impression that this cert couldn't be exploited as it is just an insecure hack to get the browser to comply even if you get a warning during development.
If having a global cert is more insecure than generating one with selfsigned, we shouldn't merge this PR. Would you explain a bit how this works?

If we don't merge this one, another option to remove selfsigned would be to deprecate this insecure feature, and add a new plugin/docs to push users to create a proper certificate, and then we can remove it in 3.0, as others are proposing in other messages

MIIEpQIBAAKCAQEAsan51A4X8wLmf/sqn/CiosFSeLnnFG3xUtSspLpudNOAu+Ai
3j8+fYpUknGztKEOZll+o55r2QNyoUgO6n7BVgixxr/iVjvs+SEVWsxYfl/jYZU5
pqJPlzqJKkvKA08kHFSQuXUyPkA0YbjSo1sJInyY2lUAxK3gSKaTUdRRpLO29Bx2
WNOPGGWAjjI85slagiskH+rXabX7Nsp0JMz4ZL0cQHgYBM97VfwumqHOVU+dWzgK
ituQDut17qFndVtQMMhQzRzz8sMb2H9i9YWTr6ateBSKHmNACaMPiYO5w8RXNggh
LhGbxUOCqb4CqB2DKc6QX4ijzdxSD7eYyNgrpQIDAQABAoIBAQCC6iFxpMD5DNYU
0FtkZ8kv7Qx8cWBZqXn980EUUVw7Me7Wgh6vbiL6FwiisK4fF96ohKHQp1gJHYVG
WvbCzIxifR2iupppuNax7TLzKUi8NqQyr+c/ZrMHWNbiF97P+W0+gwHx5u4S+PSg
XbS5MsfUZcOsfp6GTynJ3xUHY1CV76x7YI4tQ5RAWT+EEHihDA4zbh49LoqlTNOE
+pEeOF3bcqAehmLAicEfZU7iUrVKxZ9xpJebRLYBRO/MGEaXf4tp5VcTYgIlyA1p
cx8eAfKiZsiCVpTkY4lBTB9YGhfQqLr7O9aAzeCxTpjY9cJ7tUWEWv2Ybt88SSGa
7iiXLJixAoGBAOSzW6u9oxkTGY1Uy8L8SrXyox3dCwTDdqcS0okboE3dYks84g7M
knrVq3mIh8Kc6SHW2RLaxpPvSGdJywYT+sUEMwF/ZZlIzZEtXOroHgU+2L0y/9Ie
+mu6MBWEdzUWo6AmLK4XGyiqpwqDGpka0P3sai544VamInjjErRX81ebAoGBAMbf
CbnjNg0J05LizUVtXT/9v/PheofXYXEuThpGYi97HgaiOUKgIwn+o0k/o+Wxbgpq
wYTIQNQklUo4/eQbOMqRR1YVy20ylpfJOIS6AJBDKkoLBzyi5IbYspCnd25VYZ+c
MZ1jkmu6qlNXsPxtN9GLrQSluS2r07qTgfM8C92/AoGAGzyVFU3/Z5QTvfY0UiNz
zll4Pa0+i7GvptQiCLwmij9sXmOA1JxecYyS4GV3LaE8TpBkx88QSTv9Fnf+Wtn0
SpRmcq42blvyR3DsmPlN4fgLHfU97EtOLZg988IT5Fmu4PrK5WzRCjXQsPrOJx+/
dg460xAIXiZpApageqPi0jMCgYEAkNxCf28sHg88WFBw6dOiC0zRF9r0oCKZThJG
vgjq2F88HeTpBMkU/ODJHEAJKPU3jMr6r7gW0PDc9jXRaukRUyzD48b4aVBiNapT
SwsaAfuKwGiMGvUktbz/RTNXYb074UGrOlQfge10yESmHRmJgU/W9f2ph8UT82Ij
VIQ2uI0CgYEAzVpC6C/Lx9dVmE6V7evZuE3g67zlMwd7U//X5eKAp0bUnrGTmy/3
5bNHELUrVPEfG4IVIHIb1jTIJHECyTUpw0xDsVzdgsP+BShPBjiJLjMTIy/o53pX
lTNpkrXUtt5OIGC6cMx6nHuWIpfBjMa6vtfAOfzl6tt7o5v3rmaqemU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIID9zCCAt+gAwIBAgIJQD4HLTmP5m8qMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV
BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx
EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl
c3QwHhcNMjExMjE4MTEwMDI2WhcNMzExMjE2MTEwMDI2WjBpMRQwEgYDVQQDEwtl
eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD
VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsan51A4X8wLmf/sqn/CiosFS
eLnnFG3xUtSspLpudNOAu+Ai3j8+fYpUknGztKEOZll+o55r2QNyoUgO6n7BVgix
xr/iVjvs+SEVWsxYfl/jYZU5pqJPlzqJKkvKA08kHFSQuXUyPkA0YbjSo1sJInyY
2lUAxK3gSKaTUdRRpLO29Bx2WNOPGGWAjjI85slagiskH+rXabX7Nsp0JMz4ZL0c
QHgYBM97VfwumqHOVU+dWzgKituQDut17qFndVtQMMhQzRzz8sMb2H9i9YWTr6at
eBSKHmNACaMPiYO5w8RXNgghLhGbxUOCqb4CqB2DKc6QX4ijzdxSD7eYyNgrpQID
AQABo4GhMIGeMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB
BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBcBgNVHREEVTBTgglsb2NhbGhvc3SC
FWxvY2FsaG9zdC5sb2NhbGRvbWFpboIGbHZoLm1lgggqLmx2aC5tZYIFWzo6MV2H
BH8AAAGHEP6AAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBABNxJ19s
U95jrFFaH0So4mOJqtnLlm4eCCOjAhLv7r+Pyqy4ZKoXwagopk46ehx5W888lDin
Zzp2ij1o6Ry286lldT/wf8SOlWRejkb61/R8FMqu9homnD+UY3UCoLxNbkk/j9jo
xkFJPFv0RZQp0gR+F+zcvM+P62cwyLv4C5JcC2JV8GsOdg8io1RJpUxkl/SDHPXy
v7xmJuW7Tzp163adOZSack2EfFYAEI5XCzacIR9Je2vclMHNqSur0u9l67JNeO5H
r11T0xHE2WSYlMyWcjvEDX9BT/aGQnV+FPAh56loLNFdRIbOXKexH26Wkleyaaot
14Z1+PZPkw/FeLo=
-----END CERTIFICATE-----
4 changes: 3 additions & 1 deletion packages/vite/package.json
Expand Up @@ -14,7 +14,8 @@
"dist",
"client.d.ts",
"src/client",
"types"
"types",
"dev-cert.pem"
],
"engines": {
"node": ">=12.2.0"
Expand All @@ -36,6 +37,7 @@
"build-temp-types": "tsc --emitDeclarationOnly --outDir temp/node -p src/node",
"ci-build": "rimraf dist && run-s build-bundle build-types",
"patch-types": "ts-node scripts/patchTypes.ts",
"generate-selfsigned": "ts-node scripts/generateSelfsignedCertificate.ts",
"roll-types": "api-extractor run && rimraf temp",
"lint": "eslint --ext .ts src/**",
"format": "prettier --write --parser typescript \"src/**/*.ts\"",
Expand Down
80 changes: 80 additions & 0 deletions packages/vite/scripts/generateSelfsignedCertificate.ts
@@ -0,0 +1,80 @@
import fs from 'fs'
import path from 'path'
// @ts-ignore
import { generate } from 'selfsigned'

/**
* https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/createCertificate.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this link does not work, createCertificate.js does not exist in the webpack-dev-server repo current master.

Skimming the repo it looks like they only offer configuration options for bringing your own cert, no auto- or pregenerated certs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were talking with @userquin that it may also be better for us to do the same. Maybe in Vite 3.0 we can remove the autogenerated cert to push users to create secure certificates

*
* Copyright JS Foundation and other contributors
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/webpack/webpack-dev-server/blob/master/LICENSE
*/
function createCertificate() {
const pems = generate(null, {
algorithm: 'sha256',
days: 3650, // 10 years should be enough for development-only insecure certificate
keySize: 2048,
extensions: [
// {
// name: 'basicConstraints',
// cA: true,
// },
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
timeStamping: true
},
{
name: 'subjectAltName',
altNames: [
{
// type 2 is DNS
type: 2,
value: 'localhost'
},
{
type: 2,
value: 'localhost.localdomain'
},
{
type: 2,
value: 'lvh.me'
},
{
type: 2,
value: '*.lvh.me'
},
{
type: 2,
value: '[::1]'
},
{
// type 7 is IP
type: 7,
ip: '127.0.0.1'
},
{
type: 7,
ip: 'fe80::1'
}
]
}
]
})
return pems.private + pems.cert
}

const cert = createCertificate()
fs.writeFileSync(path.join(__dirname, '..', 'dev-cert.pem'), cert)
110 changes: 8 additions & 102 deletions packages/vite/src/node/http.ts
@@ -1,4 +1,4 @@
import fs, { promises as fsp } from 'fs'
import fs from 'fs'
import path from 'path'
import { Server as HttpServer } from 'http'
import { ServerOptions as HttpsServerOptions } from 'https'
Expand Down Expand Up @@ -106,8 +106,7 @@ export async function resolveHttpServer(
}

export async function resolveHttpsConfig(
https?: boolean | HttpsServerOptions,
cacheDir?: string
https?: boolean | HttpsServerOptions
): Promise<HttpsServerOptions | undefined> {
if (!https) return undefined

Expand All @@ -121,7 +120,7 @@ export async function resolveHttpsConfig(
pfx: readFileIfExists(pfx)
})
if (!httpsOption.key || !httpsOption.cert) {
httpsOption.cert = httpsOption.key = await getCertificate(cacheDir)
httpsOption.cert = httpsOption.key = getCertificate()
}
return httpsOption
}
Expand All @@ -137,104 +136,11 @@ function readFileIfExists(value?: string | Buffer | any[]) {
return value
}

/**
* https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/createCertificate.js
*
* Copyright JS Foundation and other contributors
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/webpack/webpack-dev-server/blob/master/LICENSE
*/
async function createCertificate() {
const { generate } = await import('selfsigned')
const pems = generate(null, {
algorithm: 'sha256',
days: 30,
keySize: 2048,
extensions: [
// {
// name: 'basicConstraints',
// cA: true,
// },
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
timeStamping: true
},
{
name: 'subjectAltName',
altNames: [
{
// type 2 is DNS
type: 2,
value: 'localhost'
},
{
type: 2,
value: 'localhost.localdomain'
},
{
type: 2,
value: 'lvh.me'
},
{
type: 2,
value: '*.lvh.me'
},
{
type: 2,
value: '[::1]'
},
{
// type 7 is IP
type: 7,
ip: '127.0.0.1'
},
{
type: 7,
ip: 'fe80::1'
}
]
}
]
})
return pems.private + pems.cert
}

async function getCertificate(cacheDir?: string) {
if (!cacheDir) return await createCertificate()

const cachePath = path.join(cacheDir, '_cert.pem')

try {
const [stat, content] = await Promise.all([
fsp.stat(cachePath),
fsp.readFile(cachePath, 'utf8')
])

if (Date.now() - stat.ctime.valueOf() > 30 * 24 * 60 * 60 * 1000) {
throw new Error('cache is outdated.')
}

return content
} catch {
const content = await createCertificate()
fsp
.mkdir(cacheDir, { recursive: true })
.then(() => fsp.writeFile(cachePath, content))
.catch(() => {})
return content
}
function getCertificate() {
return fs.readFileSync(
path.join(__dirname, '..', '..', '..', 'dev-cert.pem'),
'utf-8'
)
}

export async function httpServerStart(
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/preview.ts
Expand Up @@ -70,7 +70,7 @@ export async function preview(
const httpServer = await resolveHttpServer(
config.preview,
app,
await resolveHttpsConfig(config.preview?.https, config.cacheDir)
await resolveHttpsConfig(config.preview?.https)
)

// cors
Expand Down