apache / apache/linkis

[Feature][EnginePlugin] Pipeline export script allows editing of masked fields, creating data leak risk

Open
#5,311 1 comment 0 reactions 0 assignees View on GitHub
feature
Dominant language
Java
Stars
3.4k
Forks
1.2k
PR merge metrics
No merged PRs in 30d

Description

### Linkis Component

linkis-engineconn-plugins/pipeline

### What happened

**English:**

The Pipeline export script for masked plaintext fields can be edited by users. Users can modify the `without` field in the script to bypass field masking restrictions, creating a data leakage risk.

**Problem Description:**
1. Generated export scripts support editing and modification
2. Users can change the `without` field in the script to bypass plaintext field masking
3. This creates a data leakage risk and requires enhanced security controls

**Security Impact:**
- Sensitive data (e.g., passwords, personal information, financial data) can be exposed
- Compliance violations (GDPR, data privacy regulations)
- Unauthorized access to confidential information

---

**中文:**

Pipeline屏蔽明文字段的导出脚本可以被用户编辑。用户可以通过更改脚本中的`without`字段来绕过字段屏蔽限制,存在数据泄露风险。

**问题描述:**
1. 生成的导出脚本支持编辑修改
2. 用户可以更改脚本中的without字段,绕过明文字段屏蔽限制
3. 存在数据泄露风险,需要加强安全控制

**安全影响:**
- 敏感数据(如密码、个人信息、财务数据)可能被暴露
- 违反合规性(GDPR、数据隐私法规)
- 未经授权访问机密信息

### What you expected to happen

**English:**

In scenarios requiring sensitive field masking, the export script should not be editable by users to prevent sensitive data leakage:

1. **Hide the `without` field** in export scripts, preventing direct user editing
2. **Implement backend validation** to ensure masking rules remain effective even if the script is modified
3. **Use encrypted or signed scripts** to detect tampering
4. **Add permission-based controls** for field access
5. **Audit logging** for export operations with masked fields

---

**中文:**

在需要屏蔽敏感字段的场景下,导出脚本不应该被用户编辑以防止敏感数据泄露:

1. **隐藏导出脚本中的`without`字段**,防止用户直接编辑
2. **实现后端验证**,确保即使脚本被修改,屏蔽规则仍然生效
3. **使用加密或签名脚本**来检测篡改
4. **添加基于权限的控制**来访问字段
5. **审计日志**记录涉及屏蔽字段的导出操作

### How to reproduce

**English:**
1. Configure Pipeline with field masking for sensitive columns
2. Generate export script for a table with masked fields
3. Open the generated script in editor
4. Modify the `without` parameter to include previously masked fields
5. Execute the modified script
6. Observe that sensitive fields are now exported

Example vulnerable script:
```python
# User can edit this
export_data(
table="users",
without=["password", "ssn"] # User can remove items from this list
)
```

---

**中文:**
1. 配置Pipeline以屏蔽敏感列的字段
2. 为包含屏蔽字段的表生成导出脚本
3. 在编辑器中打开生成的脚本
4. 修改`without`参数以包含之前被屏蔽的字段
5. 执行修改后的脚本
6. 观察敏感字段现在被导出

易受攻击的脚本示例:
```python
# 用户可以编辑这个
export_data(
table="users",
without=["password", "ssn"] # 用户可以从此列表中删除项目
)
```

### Anything else

**English:**

**Suggested Solutions:**

**Approach 1: Backend Validation (Recommended)**
```scala
// Validate masked fields on server side
def validateExportRequest(request: ExportRequest): Boolean = {
val configuredMaskedFields = getConfiguredMaskedFields(request.table)
val requestedFields = request.fields

// Ensure masked fields are not included in export
val hasMaskedFields = configuredMaskedFields.intersect(requestedFields).nonEmpty

if (hasMaskedFields && !request.user.hasPermission("EXPORT_SENSITIVE_DATA")) {
throw new SecurityException("Attempting to export masked fields without permission")
}
true
}
```

**Approach 2: Encrypted Export Configuration**
```python
# Generate signed export configuration
import hmac
import hashlib

def generate_export_token(table, allowed_fields, secret_key):
data = f"{table}:{','.join(sorted(allowed_fields))}"
token = hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest()
return token

# On export, verify token
def verify_export_token(table, fields, token, secret_key):
expected_token = generate_export_token(table, fields, secret_key)
return hmac.compare_digest(token, expected_token)
```

**Approach 3: Remove Editable Scripts**
```scala
// Instead of generating editable scripts, use API calls with server-side enforcement
case class ExportRequest(
table: String,
exportId: String, // Generated server-side, not user-provided
checksum: String // Tamper detection
)

// Server validates all export requests
def executeExport(request: ExportRequest): Unit = {
// Validate checksum
if (!validateChecksum(request)) {
throw new SecurityException("Export request has been tampered with")
}

// Apply masking rules from server configuration
val maskedFields = getMaskedFieldsConfig(request.table)
val exportData = queryTable(request.table, exclude = maskedFields)

writeExport(exportData)
}
```

**Approach 4: Role-Based Access Control**
```sql
-- Define field-level permissions
CREATE TABLE field_permissions (
table_name VARCHAR(100),
field_name VARCHAR(100),
role_name VARCHAR(50),
permission ENUM('READ', 'MASKED', 'DENIED')
);

-- Enforce at query time
SELECT
CASE
WHEN check_permission(user_role, 'users', 'password') = 'DENIED' THEN NULL
WHEN check_permission(user_role, 'users', 'password') = 'MASKED' THEN '***'
ELSE password
END AS password
FROM users;
```

**Implementation Checklist:**
- [ ] Remove user-editable `without` field from export scripts
- [ ] Implement server-side validation for all export requests
- [ ] Add cryptographic signing/verification for export configurations
- [ ] Implement field-level permission checks
- [ ] Add audit logging for sensitive data exports
- [ ] Create unit tests for security bypasses
- [ ] Document security model for administrators

**Security Best Practices:**
1. **Principle of Least Privilege**: Users should only access fields they need
2. **Defense in Depth**: Multiple layers of validation (client + server)
3. **Audit Everything**: Log all export attempts with masked fields
4. **Regular Security Reviews**: Periodic assessment of masking effectiveness

---

**中文:**

**建议解决方案:**

**方法1:后端验证(推荐)**
```scala
// 在服务器端验证屏蔽字段
def validateExportRequest(request: ExportRequest): Boolean = {
val configuredMaskedFields = getConfiguredMaskedFields(request.table)
val requestedFields = request.fields

// 确保屏蔽字段不包含在导出中
val hasMaskedFields = configuredMaskedFields.intersect(requestedFields).nonEmpty

if (hasMaskedFields && !request.user.hasPermission("EXPORT_SENSITIVE_DATA")) {
throw new SecurityException("试图在没有权限的情况下导出屏蔽字段")
}
true
}
```

**方法2:加密导出配置**
```python
# 生成签名的导出配置
import hmac
import hashlib

def generate_export_token(table, allowed_fields, secret_key):
data = f"{table}:{','.join(sorted(allowed_fields))}"
token = hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest()
return token

# 导出时验证token
def verify_export_token(table, fields, token, secret_key):
expected_token = generate_export_token(table, fields, secret_key)
return hmac.compare_digest(token, expected_token)
```

**方法3:移除可编辑脚本**
```scala
// 不生成可编辑脚本,而是使用带服务器端强制执行的API调用
case class ExportRequest(
table: String,
exportId: String, // 服务器端生成,非用户提供
checksum: String // 篡改检测
)

// 服务器验证所有导出请求
def executeExport(request: ExportRequest): Unit = {
// 验证校验和
if (!validateChecksum(request)) {
throw new SecurityException("导出请求已被篡改")
}

// 从服务器配置应用屏蔽规则
val maskedFields = getMaskedFieldsConfig(request.table)
val exportData = queryTable(request.table, exclude = maskedFields)

writeExport(exportData)
}
```

**方法4:基于角色的访问控制**
```sql
-- 定义字段级权限
CREATE TABLE field_permissions (
table_name VARCHAR(100),
field_name VARCHAR(100),
role_name VARCHAR(50),
permission ENUM('READ', 'MASKED', 'DENIED')
);

-- 在查询时强制执行
SELECT
CASE
WHEN check_permission(user_role, 'users', 'password') = 'DENIED' THEN NULL
WHEN check_permission(user_role, 'users', 'password') = 'MASKED' THEN '***'
ELSE password
END AS password
FROM users;
```

**实施清单:**
- [ ] 从导出脚本中删除用户可编辑的`without`字段
- [ ] 为所有导出请求实现服务器端验证
- [ ] 为导出配置添加加密签名/验证
- [ ] 实现字段级权限检查
- [ ] 为敏感数据导出添加审计日志
- [ ] 创建安全绕过的单元测试
- [ ] 为管理员记录安全模型

**安全最佳实践:**
1. **最小权限原则**:用户只应访问他们需要的字段
2. **纵深防御**:多层验证(客户端+服务器)
3. **审计一切**:记录所有涉及屏蔽字段的导出尝试
4. **定期安全审查**:定期评估屏蔽有效性

### Are you willing to submit a PR?

- [x] Yes I am willing to submit a PR!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.