我有一个Rails应用程序需要允许单独的应用程序将已登录的用户与签名的JWT一起定向到我们的应用程序,在我们的应用程序中为用户创建会话,然后将用户重定向到各种用户之一应用程序中的页面。 Open redirects are bad,所以我试图弄清楚是否有可接受的最佳做法(甚至是好的建议)来处理限制Rails中的重定向网址。
一个问题是该方法理想地适用于部署在子目录中的应用程序。也就是说,应该阻止重定向到同一服务器上但位于应用程序根目录之外的位置。但这并不重要。
我已经研究过的事情:
ActionDispatch::Routing::RouteSet
有一个recognize_path
方法,但它不是公共API的一部分,甚至不被Rails路由机制使用。 ActionDispatch::Journey::Router
有一个recognize
方法,它同样不会被Rails路由机制直接调用(虽然它具有非常相似的逻辑),也不是公共API的一部分。它有一个find_routes
方法,路由机制使用 ,但它是private
方法。/../
)。 URI(root_url).merge(params[:url])
似乎解析了目录遍历,只需检查它是否以root_url
开头,这样就可以了。但我注意到/%2E%2E/
merge
将忽略/../
但仍被Apache和Chrome视为import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
Button button;
private Camera camera;
private boolean isFlashOn;
private boolean hasFlash;
Parameters params;
private Context mContext = MainActivity.this;
private static final int REQUEST = 112;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageButton imgbutton = (ImageButton)findViewById(R.id.imageButton);
hasFlash = getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
checkPermission();
//getCamera();
imgbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isFlashOn) {
turnOffFlash();
Toast.makeText(MainActivity.this, "Flash OFF!", Toast.LENGTH_SHORT).show();
imgbutton.setImageResource(R.drawable.light_off);
} else {
turnOnFlash();
Toast.makeText(MainActivity.this, "Flash ON!", Toast.LENGTH_SHORT).show();
imgbutton.setImageResource(R.drawable.light_on);
}
}
});
}
public void checkPermission() {
if (Build.VERSION.SDK_INT >= 23) {
String[] PERMISSIONS = {android.Manifest.permission.CAMERA};
if (!hasPermissions(mContext, PERMISSIONS)) {
ActivityCompat.requestPermissions((Activity) mContext, PERMISSIONS, REQUEST);
} else {
getCamera() ;
}
} else {
getCamera() ;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getCamera() ;
} else {
Toast.makeText(mContext, "The app was not allowed to access camera", Toast.LENGTH_LONG).show();
}
}
}
}
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
private void getCamera() {
if (camera == null) {
try {
camera = Camera.open();
params = camera.getParameters();
}catch (Exception e) {
}
}
}
private void turnOnFlash() {
if(!isFlashOn) {
if(camera == null || params == null) {
return;
}
params = camera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(params);
camera.startPreview();
isFlashOn = true;
}
}
private void turnOffFlash() {
if (isFlashOn) {
if (camera == null || params == null) {
return;
}
params = camera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(params);
camera.stopPreview();
isFlashOn = false;
}
}
@Override
protected void onDestroy(){
super.onDestroy();
if(camera!=null){
camera.stopPreview();
camera.setPreviewCallback(null);
camera.release();
camera = null;
}
}
protected void onStop() {
super.onStop();
}
@Override
protected void onResume() {
super.onResume();
if(hasFlash)
turnOnFlash();
}
@Override
protected void onPause() {
super.onPause();
}
}
(至少)。我不确定是否还有其他更明显的边缘情况。如果有更好的方法来处理这种情况,而不是将重定向网址作为用户提供的参数,我也很高兴听到这种情况。虽然通过这种方法访问Rails应用程序中的每个页面都不是必不可少的,但每次需要允许其他页面时我也不想付出很多努力。
答案 0 :(得分:0)
经过一番研究后,我决定选择URI#merge
选项,并使用额外的替代来处理百分比编码问题。有点像:
normalized_url = params[:url].gsub(/%2E/i, '.')
merged_url = URI(root_url).merge(normalized_url).to_s
merged_url.start_with?(root_url)
根据section 2.3 of RFC 3986,句点是"无保留的字符," "将替换未保留字符与其对应的百分比编码的US-ASCII八位字节不同的URI是等效的。"所以我有理由确定用句点替换%2E
和%2e
。
同一RFC的第6节提到通过以下方式规范化通用URI:
.
和..
这涵盖了通用URI的情况,RFC 7230似乎没有专门为HTTP和HTTPS URI添加与此情况相关的任何内容。所以我觉得我正在讨论最可能出现的问题,但我无法确定是否有一些我曾经错过的客户或服务器的边缘情况。