我计划运行有潜在危险的应用程序(浏览器等)在一台单独的 X 服务器上,但由于每个服务器都有自己的剪贴板,因此不可能将链接/文本从一个窗口复制到另一个窗口。
大多数文章建议使用 xclip 和其他类似实用程序的脚本来执行此操作。
但如何正确地创建一个通用剪贴板以免意外创建新漏洞?
答案1
注意:更新答案以反映策略的变化 - 我不是在服务器/容器上运行 Xephyr,而是在主机/主要使用环境上运行它。原因是在服务器/容器上运行 Xephyr 就像在内门而不是前门上加锁 - 不良行为者会绕过内门并直接通过 X 管道远程套接字访问剪贴板。
面对同样的剪贴板漏洞问题,我通过在主机(我的个人工作空间)上运行 Xephyr,并将 X 从容器上的服务器转发到本地 Xephyr 来解决这个问题。
Xephyr 在显示器“:2”上运行,而我的个人工作区窗口和浏览器在默认显示器“:0”上运行。这两个显示器不共享剪贴板 - 每个显示器都有自己的剪贴板。这是防止在显示器“:0”上窥探我的个人工作区剪贴板的唯一方法。然后我设置了热键(例如功能键),一个用于将剪贴板内容从“:0”传输到“:2”,另一个用于“:2”到“:0”,因此可以完全控制。
在 shell 脚本中,代码可能如下所示
xsel --display :0 --clipboard -o | xsel --display :2 --clipboard -i
尽管我使用的是 javascript,如本文底部所示。
启动 X 转发的 Shell 脚本可能如下所示
Xephyr <args> :2
DISPLAY=:2 ssh -X -R 44713:localhost:4713 user@container <<EOF
DISPLAY=:10 PULSE_SERVER=tcp:localhost:44713 openbox --startup firefox
EOF
虽然我使用的是 javascript 程序来做到这一点。
下面是在 ':0' 和 ":2" 之间复制的 javascript 代码,通过热键映射到该代码。您可以看到它弹出一个临时消息框以确认其有效。
#!/usr/bin/env node
`strict`;
const child_process = require('child_process');
// TODO: write unviewable error messasges to system log
function notifySend(title, msg){
title = title.replace(/"/g, '\\"');
msg = msg.replace(/"/g, '\\"');
//msg = msg.replace(/'/g, '\\'')
try {
child_process.execSync(`notify-send "${title}" "${msg}"`);
} catch(e) {
console.log(`system call "notify-send" failed with ${e.message}`);
}
}
async function clipXfer(fromDispNum,toDispNum){
var clipValue;
let cmd1 = `xsel --display :${fromDispNum} --clipboard --output`;
let cmd2 = `xsel --display :${toDispNum} --clipboard --input`;
try {
clipValue = child_process.execSync(cmd1, { encoding: 'utf-8' });
} catch(e) {
throw Error(`Display ${fromDispNum} clipboard is empty`);
}
await new Promise((resolve, reject)=>{
// eslint-disable-next-line no-unused-vars
var proc = child_process.exec(cmd2, (error, stdout, stderr) => {
//if (stdout) console.log(stdout);
//if (stderr) console.log(stderr);
if (error) {
reject(Error(`${error.message}`));
}
resolve();
});
if (!proc.stdin.write(clipValue))
reject(Error('clipToCont(), pipe write failed'));
proc.stdin.end();
});
}
async function main()
{
let argOff=2;
if (process.argv.length-argOff<2)
throw Error('clip-xfer requires two arguments: fromDispNum, toDispNum');
let fromDispNum = process.argv[argOff];
let toDispNum = process.argv[argOff+1];
argOff+=2;
let f = (outcome)=>{
notifySend("clipXfer", `${fromDispNum} => ${toDispNum} ${outcome}`);
};
await clipXfer(fromDispNum,toDispNum)
.then(f('SUCCESS'))
.catch((e)=>{f(`FAILURE, ${e.message}`); throw e;});
}
//console.log(process.argv);
//console.log(process.env);
main()
.then(()=>{process.exitCode=0;})
.catch((e)=>{
console.log(e);
process.exitCode=1;
});